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