1. 程式人生 > 實用技巧 >Yoi #371 梯度彌散

Yoi #371 梯度彌散

題面


分析

考試時題目看錯白白耗了一個小時(tmd下次在看錯我就***)
顯然二分
二分之後可以通過差分的手段\(O(n)\)判斷是否可行

  • \(1=1+0\)
  • \(x=(x+1)-1\)
  • \(x^2=(x+1)^2-2x-1\)
    用陣列維護即可
#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N=1e6+5;
int n,C,K;
ll a[N],f[N],g[N],h[N];

inline bool check(int x) {
	int ret=0;
	if(C==0) {
		for(int i=1;i<=n;i++) {
			f[i]=0;
		}
		for(int i=1;i<=n;i++) {
			f[i]+=f[i-1];
			if(f[i]<a[i]) {
				ll t=a[i]-f[i];
				f[i]+=t;
				f[i+x+1]-=t;
				ret+=t;
				if(ret>K) return 0;
			}
		}
		return 1;
	} else if(C==1) {
		for(int i=1;i<=n;i++) {
			f[i]=g[i]=0;
		}
		for(int i=1;i<=n;i++) {
			f[i]+=f[i-1];
			g[i]+=g[i-1]-f[i];
			if(g[i]<a[i]) {
				ll t=(a[i]-g[i]-1)/x+1;
				f[i]+=t;
				g[i]+=t*x;
				f[i+x+1]-=t;
				ret+=t;
				if(ret>K) return 0;
			}
		}
	} else {
		for(int i=1;i<=n;i++) {
			f[i]=g[i]=h[i]=0;
		}
		for(int i=1;i<=n;i++) {
			f[i]+=f[i-1];
			g[i]+=g[i-1]-f[i];
			h[i]+=h[i-1]-2*g[i]-f[i];
			if(h[i]<a[i]){
				ll t=(a[i]-h[i]-1)/(x*x)+1;
				f[i]+=t;
				g[i]+=t*x;
				h[i]+=t*x*x;
				f[i+x+1]-=t;
				ret+=t;
				if(ret>K) return 0;
			}
		}
	}
	return ret<=K;
}
int main() {
	freopen("dispersion.in","r",stdin);
	freopen("dispersion.out","w",stdout);
	int Num; scanf("%d%d%d%d",&Num,&n,&C,&K);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	int l=0,r=1e6,ans=-1;
	while(l<=r) {
		int mid=l+r>>1;
		if(check(mid)) ans=mid,r=mid-1;
			else l=mid+1;
	}
	printf("%d\n",ans);
	return 0;
}