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