P7287 「EZEC-5」魔法(贪心&二分)

P7287 「EZEC-5」魔法(贪心&二分)


思路

一开想枚举答案区间 [ l , r ] [l,r] [l,r],发现这样枚举会炸。
正解是枚举操作次数,而且注意到最优的操作肯定是先 + + +再乘的。
所以我们可以枚举乘的次数,然后二分加的次数,至于找最大区间就可以贪心, O ( n ) O(n) O(n)求出最大字段和即可。

注意二分的下界是0,上界不要太大,不然会爆 l o n g   l o n g long\ long long long,也不要太小。
时间复杂度: O ( n l o g 2 s ) O(nlog^2s) O(nlog2s)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
int n,A,B,s;
int a[N];
bool check(ll x,ll y){
	ll sum=0;
	for(int i=1;i<=n;i++){
		if(sum<0) sum=0;
		sum+=1LL*(a[i]+x)*y;
		if(sum>=s) return true; 
	}
	return false;
}
int main(){
	scanf("%d%d%d%d",&n,&A,&B,&s);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	int cnt=log2(s)+1;
	ll ans=1e18,mu=1;
	for(int i=0;i<=cnt;i++,mu<<=1){
		ll l=0,r=2e9;
		while(l<=r){
			ll m=l+r>>1;
			if(check(m,mu)) r=m-1;
			else l=m+1;
		}
		if(check(l,mu)) ans=min(ans,1LL*l*A+1LL*i*B); 
	}printf("%lld\n",ans);
	return 0;
}
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页