1. 程式人生 > 其它 >CF1540C2. Converging Array (Hard Version)

CF1540C2. Converging Array (Hard Version)

長度分別為\(n\)\(n-1\)的陣列\(a_i,b_i\)

\(a_i\)進行無限次操作:隨機選擇\(i\in [1,n)\),令\(a_i= \min(a_i,\frac{a_i+a_{i+1}-b_i}{2}),a_{i+1}=\max(a_{i+1},\frac{a_i+a_{i+1}+b_i}{2})\)

可證\(a_i\)收斂。

\(F(a,b)\)表示\(a_1\)收斂的結果。

給定\(b_i\)\(c_i\)(表示\(a_i\in[0,c_i]\))。\(Q\)次詢問,每次問\(F(a,b)\ge x\)\(a_i\)的方案數。


考慮一次有變化的操作的條件,發現不管是\(min\)

還是\(max\)都是\(b_i>a_{i+1}-a_i\)

\(t_i=a_{i+1}-a_i\)。發現一次操作相當於:如果\(t_i<b_i\)\(t_{i-1}+=\frac{t_i-b_i}{2},t_{i+1}+=\frac{t_i-b_i}{2},t_i=b_i\)

形象一下:可以看成\(t_i\)多出\(b_i\)的部分(儘管是負的)平均分配到兩邊,微分一下,某個\(\epsilon\)\(t_i<b_i\)的時候可以向左右等概率隨機遊走。要麼隨機到\(t_i\ge b_i\),要麼掉到\(t_0\)\(t_n\)

如果\(\forall t_i\le b_i\)

,感受到最終會變成所有\(t_i=b_i\)\(i\in[1,n)\)),答案即\(t_0\)很好求。擴充套件到所有情況,發現:只需要列舉\(L=[1,n]\),只考慮\((0,L)\)(此時忽略\(t_i<b_i\)的限制),不能操作\(t_0\)\(t_L\),此時\(t_0\)。列舉\(L\)\(t_0\)的最小值,就是\(t_0\)真正的值。

證明考慮在算\(L\)的時候,考慮\((0,L)\)。如果事實上\((0,L-1)\)中最終出現了坑(即\(t_i>b_i\)的位置),那麼一定存在某個字首算過正確答案,並字首保留了它作為答案。如果到最後\(t_{L-1}=b_{L-1}\),那麼在過程中\(t_{L-1}\)

可能為填坑作出過貢獻但沒有將坑填完(否則就是下一種情況),因為坑沒填完所以貢獻不到\(t_0\)所以不會更新答案;否則有正貢獻不更新答案。如果最終沒有出現坑,如果到最後\(t_{L-1}=b_{L-1}\)則有負貢獻能更新答案;否則產生新坑有正貢獻不更新答案。

考慮帶吸收壁的隨機遊走問題。一個長度為\(L\)的線段上,到點\(0\)和點\(L\)結束,考慮到\(0\)的概率。設\(f_i\)\(f_0=1,f_L=0,f_i=\frac{f_{i-1}+f_{i+1}}{2}\),顯然\(f_i=\frac{L-i}{L}\)

於是條件為:\(t_0+\min_L \sum_{i=1}^{L-1}(t_i-b_i)\frac{L-i}{L}\ge x\)

展開一下,發現其實就是:\(\forall L,\sum_{i=1}^L a_i\ge Lx+\sum_{i=1}^{L-1}b_i(L-i)\)

DP記一下字首和。直接做\(O(n^2V^2)\),可以用字首和優化至\(O(n^2V)\)

然後觀察下哪些\(x\)是“有用的”。令\(h_L=Lx+\sum_{i=1}^{L-1}b_i(L-i)\)。首先如果有解就要\(\forall L,Lx+h_L\le LV\),即\(x\le V-\max{\frac{h_L}{L}}\)。另外如果\(\forall Lx+h_L<0\),則答案和\(x\)的取值無關,算一下\(x<-\max\frac{h_L}{L}\),夾在兩者之間的只有\(O(V)\)個。於是只需要算\(O(V)\)\(x\)的答案即可。


using namespace std;
#include <bits/stdc++.h>
const int N=105,mo=1000000007;
typedef long long ll;
int n;
int c[N],b[N];
int sc[N],sb0[N],sb1[N],h[N];
int lim;
int f[N][N*N],g[N*N];
int ans[N];
int solve(){
	memset(f,0,sizeof f);
	f[0][0]=1;
	for (int i=1;i<=n;++i){
		ll ps=0;
		for (int s=0;s<=sc[i];++s)
			g[s]=(ps+=f[i-1][s])%=mo;
		for (int s=sc[i];s>c[i];--s)
			g[s]=(g[s]-g[s-c[i]-1]+mo)%mo;
		int t=max(lim*i+h[i],0);
		for (int s=t;s<=sc[i];++s)
			f[i][s]=g[s];
	}
	ll ans=0;
	for (int s=0;s<=sc[n];++s)
		ans+=f[n][s];
	ans%=mo;
	return ans;
}
int main(){
	//freopen("in.txt","r",stdin);
	scanf("%d",&n);
	for (int i=1;i<=n;++i){
		scanf("%d",&c[i]);
		sc[i]=sc[i-1]+c[i];
	}
	for (int i=1;i<n;++i)
		scanf("%d",&b[i]);
	int mx=INT_MIN;
	for (int i=1;i<=n;++i){
		sb0[i]=sb0[i-1]+b[i];
		sb1[i]=sb1[i-1]+b[i]*i;
		h[i]=sb0[i]*i-sb1[i];
		mx=max(mx,(h[i]+i-1)/i);
	}
	for (int i=0;i<=101;++i)
		lim=i-mx,ans[i]=solve();
	int Q;
	scanf("%d",&Q);
	while (Q--){
		scanf("%d",&lim);
		lim=min(max(lim,0-mx),101-mx);
		printf("%d\n",ans[lim+mx]);
	}
	return 0;
}