题目大意:给你n,a,b,s四个数,其中n代表有n个输入的数字
你可以任意对这些数字的任意连续区间进行全部加1或者乘*2的操作
其中a代表每次加1的消耗值,b代表每次乘2的消耗值
问这些数字的和大于等于s所需要的最小消耗值是多少
(详情见题目)
思路:
首先不难想到每次加1的时候肯定是全部数字加1是最优的选择
但一共有1e9中加1的情况,枚举肯定不行
那么乘2呢?
这里数据范围是1e9是很关键的一点,说明如果是正数,它最多乘2的31次方就可以达到s了
如果是负数那怎么也达不到
这时候我们发现对于乘2可以找一个最大子段和然后进行枚举乘31次就好了
既然知道了乘2的时候是枚举31次
那么当知道乘2的操作后我们就可以利用二分去查找加1的情况
最后再把操作次数计算出来就好了
所以总的思路是枚举+二分
代码:
#include <iostream>
#include <math.h>
using namespace std;
int c[200010];
int n;
long long solve(long long x) //求最大子段和
{
long long ans=-1e10,cnt=0;//这里要是负数!我WA了三个测试点就是WA这里
for(int i=0; i<n; i++)
{
if(cnt<0)cnt=0;
cnt+=c[i]+x;
ans=max(ans,cnt);
}
return ans;
};
int main()
{
long long ans=1e19;
long long a,b,s;
cin>>n>>a>>b>>s;
for(int i=0; i<n; i++)cin>>c[i];
for(int i=0; i<32; i++)//枚举
{
long long mid;
long long l=0,r=1e19;
while(l<=r){//二分查找对应的加1次数
mid=l+(r-l)/2;
if(solve(mid)>=ceil(1.0*s/(1<<i))){//看最大子段和是不是大于等于s/2的i次方就好
r=mid-1;
}else {
l=mid+1;
}
}
ans=min(ans,(long long)a*l+b*i);//这里也要记得开long long
}
cout<<ans<<endl;;
return 0;
}

京公网安备 11010502036488号