shot【题解】【考试】

9 篇文章 0 订阅
3 篇文章 0 订阅

前言

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(Tx)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]的概率转移过来。就是

dp[i][l][r]=g[l][r]×l,r[l,r][l,r]Φdp[i1][l][r] d p [ i ] [ l ] [ r ] = g [ l ] [ r ] × ∑ l ′ , r ′ [ l , r ] ⋂ [ l ′ , r ′ ] ≠ Φ d p [ i − 1 ] [ 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]
具体方程就是

l,r[l,r][l,r]Φdp[i1][l][r]=sum[m][m]sum[l1][l1]sum[mr][mr] ∑ l ′ , r ′ [ l , r ] ⋂ [ l ′ , r ′ ] ≠ Φ d p [ i − 1 ] [ l ′ ] [ r ′ ] = s u m [ m ] [ m ] − s u m [ l − 1 ] [ l − 1 ] − s u m [ m − r ] [ m − r ]

其中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就麻烦

dp[r]=g[nr]×l=1lrg[l1]×(sum[m]sum[l1]sum[mr]) d p [ r ] = g [ n − r ] × ∑ l = 1 l ≤ r g [ l − 1 ] × ( s u m [ m ] − s u m [ l − 1 ] − s u m [ m − r ] )

总是意思就是枚举l这相当于复杂度没变。
但是可以拆开,发现,有的与枚举l无关,有的有关
g[nr]×(sum[m]sum[mr])×(l=1lrg[l1])(l1lrg[l1]×sum[l1]) g [ n − r ] × ( s u m [ m ] − s u m [ m − r ] ) × ( ∑ l = 1 l ≤ r g [ l − 1 ] ) − ( ∑ l − 1 l ≤ r g [ l − 1 ] × s u m [ l − 1 ] )

然后分别维护一下关于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];
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值