CF708E Student's Camp
看题解人 所以本地写了一遍然后放在cnblog上选了“只有我”的权限在cnblog修好后会不会显示。。。
网格始终保持联通,等价于要求 \(k\) 天后,任意相邻两行剩下的区间有交。
设 \(f_{i,l,r}\) 表示 \(1\) 到 \(i\) 行符合要求且第 \(i\) 行剩下的砖是 \([l,r]\) 的概率。
考虑风对于指定的某一行的某一边吹掉 \(i\) 块砖的概率:
\[D(i)={k\choose i }p^i(1-p)^{k-i}
\]
考虑特定的一行剩下 \([l,r]\) 的概率:
\[P(l,r)=D(l-1)D(m-r)
\]
考虑计算 \(f_{i,l,r}\) :
\[f_{i,l,r}=P(l,r)\sum_{[l',r']∩[l,r]\neq ∅}f_{i-1,l',r'}\\
f_{i,l,r}=D(l-1)D(m-r)(\sum_{l'\le r'} f_{i-1,l',r'}-\sum_{r'<l} f_{i-1,l',r'}-\sum_{l'>r} f_{i-1,l',r'})
\]
复杂度 \(O(nm^2)\) 需要优化。
\[F(i)=\sum_{l\le r} f_{i,l,r}\\
L(i,x)=\sum_{l\le r<x} f_{i,l,r}=\sum_{r=1}^{x-1} \sum_{l=1}^r f_{i,l,r}\\
R(i,x)=\sum_{r\ge l>x} f_{i,l,r}=\sum_{l=x+1}^m \sum_{r=l}^m f_{i,l,r}\\
f_{i,l,r}=D(l-1)D(m-r)(F(i-1)-L(i-1,l)-R(i-1,t))\\
\]
显然 \(L\) 和 \(R\) 非常对称。 \(L(i,x)=R(i,m+1-x)\) 因此这里只考虑 \(L\) 的求解。
令 \(S_L(i,r)\) 为右端点为 \(r\) 的 \(f_{i,l,r}\) 之和。
则
\[L(i,x)=\sum_{r=1}^{x-1} S_L(i,r)
\\F(i)=\sum_{r=1}^m S_L(i,r)
\]
\[\begin{aligned}
S_L(i,r)&=\sum_{l\le r} f_{i,l,r}\\
&=\sum_{l\le r} D(l-1)D(m-r)(F(i-1)-L(i-1,l)-R(i-1,r))\\
&=D(m-r)\sum_{l\le r} D(l-1)(F(i-1)-L(i-1,l)-R(i-1,r))\\
&=D(m-r)( \ (F(i-1)-R(i-1,r))\sum_{l\le r} D(l-1)-\sum_{l\le r} (D(l-1)L(i-1,l))\ )
\end{aligned}
\]
因此对 \(D(l-1)\) 和 \(D(l-1)L(i-1,l)\) 做前缀和 ,计算出 \(S_L(i,r)\) ,计算出 \(L(i,l)\),以此类推,直到算出最终答案。
时间复杂度 \(O(nm)\) ,空间复杂度通过滚动可以优化为 \(O(m)\) (可以,但没必要)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1510,K=100010,mod=1e9+7;
int n,m,a,b,k;
int tmpp[K],tmp1p[K],C[K],d[K],sum[K];
int sumdl[N],tmpsum[N],s[N][N],F[N],L[N][N];
int power(int x,int y){
int ret=1;
while(y){
if(y&1) ret=1ll*ret*x%mod;
x=1ll*x*x%mod; y>>=1;
}
return ret;
}
int main(){
// freopen("ex.in","r",stdin);
//freopen("ex.out","w",stdout);
scanf("%d%d%d%d%d",&n,&m,&a,&b,&k);
int p=1ll*a*power(b,mod-2)%mod;
tmpp[0]=tmp1p[0]=C[0]=1;
for(int i=1;i<=k;i++){
tmpp[i]=1ll*tmpp[i-1]*p%mod;
tmp1p[i]=1ll*tmp1p[i-1]*((1+mod-p)%mod)%mod;
}
for(int i=0;i<=min(m,k);i++){
if(i) C[i]=1ll*(k-i+1)*power(i,mod-2)%mod*C[i-1]%mod;
d[i]=1ll*C[i]*tmpp[i]%mod*tmp1p[k-i]%mod;
}
for(int i=0;i<=m;i++) sum[i]=(i)?((sum[i-1]+d[i])%mod):d[i];
s[0][m]=1; F[0]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int tmp=(1ll*((F[i-1]+mod-L[i-1][m+1-j])%mod)*sum[j-1]%mod+mod-sumdl[j])%mod;
s[i][j]=1ll*d[m-j]*tmp%mod;
tmpsum[j]=(tmpsum[j-1]+s[i][j])%mod;
//求s
}
for(int j=1;j<=m;j++){
L[i][j]=tmpsum[j-1];
sumdl[j]=(sumdl[j-1]+1ll*d[j-1]*L[i][j]%mod)%mod;
}
F[i]=tmpsum[m];
}
printf("%d\n",F[n]);
return 0;
}