题目大意:给你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; }