前言
CF上的被搞成了考题
题面
一座墙,有n+2层。每一分钟墙的左边与右边都有a/b的概率被炸掉一块砖。每一层有m块砖。第一层和第n+2层是底和顶不能被炸。
现在求T分钟后墙还没有倒的概率
答案对1e9+7取膜
n,m<1500,t<1e7
sol
解这个题要用到一些
Σ
Σ
。
这个题一看就知道层与层之间独立,每一层打掉成什么样很好求。
比如说打成了[l,r]这样,就是左边打成这样的概率乘上右边打成这样的概率。无论那一边,都是打掉x块的概率,比如这里就是p[l-1]×p[n-r]。
以下设p1为打掉的概率(即a/b),p2=1-p1.
p怎么求?
p[x]是打掉x块的概率,所以
p[x]=CTx×px1×p(T−x)2
p
[
x
]
=
C
x
T
×
p
1
x
×
p
2
(
T
−
x
)
然后就可以预处理p,接下来把p叫做g,g[l][r]表示打成[l,r]的概率,g[x]表示打掉x的概率。
然后我们发现,如果设有一个
dp[i][l][r]
d
p
[
i
]
[
l
]
[
r
]
为到了第i层,剩下[l,r]的概率。转移很简单的就是由i-1的任何一个与[l ,r]有交的区间乘上这一层被打成[l,r]的概率转移过来。就是
很烦躁对吧,如果记sum是一个dp[i-1]的前缀和,那么就可以用sum来代替。
但是还是很复杂对吧,而且在时间上没有优化,但注意到,如果你去用sum代替,还是会发现最终只要sum[l][l]和sum[r][n]的。
没什么用对吧,但是你会发现,最终就是总体的m×m的矩形减去左上角的和右下角的。左上角是一个sum[i][i]的形式对吧,右下角好丑对吧。
但是右下角和左上角是等效的。因为比如dp[1][2](省略第一维)和dp[n-1][n]是一样的,因为对称。所以说右下角那个其实可以对称到左上角。这样就也是一个sum[i][i]
具体方程就是
其中sum[n-r][n-r]就是下面那个正方形翻上去
也就是说我们用一个sum[i]表示sum[i][i]就可以了,这样二维变一维,三方变平方。
但是dp的状态还是二维,但是,在考虑构造每一层的sum[i]时,发现只要dp[l][r]的r小于等于i就可以加进去,跟l没有关系,所以就懒得要l了。所以dp[r]就代替了dp[l][r]。
dp[r]转移给自己这一层的sum[r]很容易,直接赋值,最后在对sum进行扫一遍来求前缀和。但是上一层的sum转移给dp就麻烦
总是意思就是枚举l这相当于复杂度没变。
但是可以拆开,发现,有的与枚举l无关,有的有关
然后分别维护一下关于g的前缀和,sum×g的前缀和就可以了
code
#include<bits/stdc++.h>
using namespace std;
inline char gc(){
static char buf[1<<6],*p1=buf,*p2=buf;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,1<<6,stdin),p1==p2)?EOF:*p1++;
}
template <class T>
inline void read(T&data){
data=0;
register char ch=0;
while(ch<'0'||ch>'9')ch=getchar();
while(ch<='9'&&ch>='0'){
data=(data<<3)+(data<<1)+(ch&15);
ch=getchar();
}
return;
}
const int mod= 1e9+7,_ = 1511;
typedef long long LL;
int n,m,g[_],jc[10000100],Tc,sum[_][_],gs[_][_],gsum[_];
inline int poww(register int a ,register int b){
register int ret=1,Base=a;
for(;b;b>>=1){
if(b&1)ret=1LL*ret*Base%mod;
Base=1LL*Base*Base%mod;
}
return ret;
}
inline int C(register int a,register int b){
return 1LL*jc[a]*poww(jc[b],mod-2)%mod*poww(jc[a-b],mod-2)%mod;}
int main(){
freopen("shot.in","r",stdin);
freopen("shot.out","w",stdout);
read(n),read(m);
register int a,b;
read(a),read(b);
read(Tc);
register int p1 = 1LL*a*poww(b,mod-2)%mod, p2 = 1LL*(b-a)*poww(b,mod-2) %mod;
jc[0]=1;
for(register int i=1;i<=Tc;++i)jc[i]=1LL*jc[i-1]*i%mod;
for(register int i=0;i<=min(m,Tc);++i)
g[i]=1LL*C(Tc,i)*poww(p1,i)%mod*poww(p2,Tc-i)%mod;
gsum[0]=g[0];
for(register int i=1;i<=m;++i)gsum[i]=(g[i]+gsum[i-1])%mod;
for(register int r=1;r<=m;++r)
for(register int l=1;l<=r;++l)
sum[1][r]+=1LL*g[l-1]*g[m-r]%mod,sum[1][r]%=mod;
for(register int i=1;i<=m;++i)sum[1][i]=(sum[1][i-1]+sum[1][i])%mod;
for(register int i=1;i<=m;++i)gs[1][i]=(gs[1][i-1]+1LL*g[i]*sum[1][i]%mod)%mod;
for(register int i=2;i<=n;++i){
int now = i%2,pre = (i-1)%2;
for(register int r=1;r<=m;++r){
register int Ri = m-r;
sum[now][r]=1LL*g[Ri]*(1LL*(sum[pre][m]-sum[pre][m-r]+mod)*gsum[r-1]%mod-gs[pre][r-1]+mod)%mod;
}
for(register int j=1;j<=m;++j)sum[now][j]=(sum[now][j-1]+sum[now][j])%mod;
for(register int j=1;j<=m;++j)gs[now][j]=(gs[now][j-1]+1LL*g[j]*sum[now][j]%mod)%mod;
}
cout<<sum[n%2][m];
}