1. 程式人生 > 其它 >CF708E Student's Camp 題解

CF708E Student's Camp 題解

狀態優化+字首和優化

Statement

有一個 \((n+2) \times m\) 的網格。

除了第一行和最後一行,其他每一行每一天最左邊和最右邊的格子都有 \(p\) 的概率消失。

\(k\) 天后,網格始終保持連通的概率。

\(n,m \le 1.5 \times 10^3\)\(k \le 10^5\),取模 \(10^9+7\)

Solution

首先應該發現的是對於每一個行都是獨立的,長什麼樣子全憑自己

同時也發現了對於一行而言,左右怎麼削也是獨立的,左邊不會干擾右邊,右邊同理

可以想到設 \(f[i][l][r]\) 表示前 \(i\) 行連通,交集為 \([l,r]\) 的概率,有暴力轉移:

為了方便起見,下面設 \(g[i]=\binom ki p^i(1-p)^{k-i}\)

\[f[i][l][r]=\binom k{l-1}\binom k{m-r}p^{m-r+l-1}(1-p)^{2k-m+r-l+1}\sum_{l^{\prime}\le r^{\prime},\max(l,l^{\prime})\le \min(r,r^{\prime})} dp[i-1][l^{\prime}][r^{\prime}]\\ =g[l-1]g[m-r]\sum dp[i-1][l^{\prime}][r^{\prime}] \]

好,\(O(n^3)\) 狀態,\(O(n^2)\)

轉移,很有精神。

不管怎樣,狀態數都是要削的,不然削轉移複雜度沒有意義。

本題最為神仙的一步出現了,考慮到 \(f[i][l][\dots]\) 其實和 \(f[i][\dots][m-l+1]\) 等價(反正左邊和右邊都是獨立的,那麼左右就是對稱的,換一下無所謂),

我們考慮直接改設 \(f[i][r]\) 表示前 \(i\) 行聯通,交集為 \([\dots ,r]\) 的概率和

粗暴地,管都不管,直接先來一個

\[\sum_{r^{\prime}=1}^mf[i-1][r^{\prime}]\to f[i][r] \]

考慮多算了什麼東西,多算了

  • \(r^{\prime}<l\)
    ,可以看作是所有以 \(l\) 為右端點的區間的貢獻,也就是 \(\sum_\limits{j=1}^{l-1}f[i-1][j]\)
  • \(r<l^{\prime}\),可以看作是所有以 \(r\) 為左端點的區間的貢獻,對稱一下,也就是 \(\sum_\limits{j=1}^{m-r}f[i-1][j]\)

所以列舉一下 \(l\),式子變成了這樣:

\[f[i][r]=\sum_{l=1}^r g[l-1]g[m-r](\sum_{j=1}^mf[i-1][j]-\sum_\limits{j=1}^{l-1}f[i-1][j]-\sum_\limits{j=1}^{m-r}f[i-1][j]) \]

暴力計算的話,狀態數 \(O(n^2)\) ,轉移 \(O(n^2)\) ,很好,幹掉一個 \(O(n)\)

發現括號裡面顯然是一個字首和的形式,記 \(F[j]=\sum_{k\le j} f[i-1][k]\)

\[f[i][r]=\sum_{l=1}^r g[l-1]g[m-r](F[m]-F[l-1]-F[m-r]) \]

好了,轉移 \(O(n)\) 了,但是這個式子顯然還是很有優化的潛力的,從上面對算重部分的分析可以看出,其實我們真正需要列舉的 \(l\) 的部分只有 \(F[l-1]\) ,其他大致與 \(l\) 關係不是很大

運用小學知識拆括號併合並同類項:

\[f[i][r]=g[m-r]((F[m]-F[m-r])\sum _{l=1}^rg[l-1]-\sum_{l=1}^rg[l-1]F[l-1]) \]

\(G[j]=\sum_{k\le j} g[k],FG[j]=\sum_{k\le j}g[j]F[j]\)

\[f[i][r]=g[m-r]\times ((F[m]-F[m-r])G[r-1]-FG[r-1]) \]

好了,轉移 \(O(1)\) 了,狀態數 \(O(n^2)\)\(F,G,FG\) 複雜度也是 \(O(n^2)\)

總複雜度 \(O(n^2)\) (突然發現自己在分析複雜度的時候把 \(nm\) 混用了,無所謂)

本題的關鍵在於深入理解左右獨立性質之後對於狀態的精彩縮減,後續的字首和優化可以在嘗試性地拆括號後發現

Code

#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
const int K = 1e5+5;
const int N = 3005;

char buf[1<<23],*p1=buf,*p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read(){
    int s=0,w=1; char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
    while(isdigit(ch))s=s*10+(ch^48),ch=getchar();
    return s*w;
}
inline void inc(int &a,int b){a=a>=mod-b?a-mod+b:a+b;}
inline void dec(int &a,int b){a=a>=b?a-b:a+mod-b;}
inline int add(int a,int b){return a>=mod-b?a-mod+b:a+b;}
inline int red(int a,int b){return a>=b?a-b:a+mod-b;}
int ksm(int a,int b){
    int res=1;
    while(b>0){
        if(b&1)res=1ll*res*a%mod;
        a=1ll*a*a%mod,b>>=1;
    }
    return res;
}

int f[N],g[N],F[N],G[N],FG[N];
int jc[K],invj[K];
int n,m,p,b,k;

int C(int n,int m){
    if(n<m||m<0)return 0;
    return 1ll*jc[n]*invj[m]%mod*invj[n-m]%mod;
}
void prework(){
    n=read(),m=read(),p=read(),b=read(),k=read();
    p=1ll*p*ksm(b,mod-2)%mod;
    for(int i=jc[0]=1;i<K;++i)jc[i]=1ll*jc[i-1]*i%mod;
    invj[K-1]=ksm(jc[K-1],mod-2);
    for(int i=K-2;~i;--i)invj[i]=1ll*invj[i+1]*(i+1)%mod;
    for(int j=0;j<=m;++j)
        g[j]=1ll*C(k,j)*ksm(p,j)%mod*ksm(red(1,p),k-j)%mod;
    G[0]=g[0];
    for(int j=1;j<=m;++j)
        G[j]=add(G[j-1],g[j]);
}

signed main(){
    prework();
    F[m]=1,FG[m]=g[m]*F[m];
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j)
            f[j]=1ll*g[m-j]*red(1ll*red(F[m],F[m-j])*G[j-1]%mod,FG[j-1])%mod;
        for(int j=1;j<=m;++j)
            F[j]=add(F[j-1],f[j]),
            FG[j]=add(FG[j-1],1ll*g[j]*F[j]%mod);
    }
    printf("%d\n",F[m]);
    return 0;
}