[2017集訓隊作業自選題#149]小c的島嶼

前言

感覺這題挺棒棒的。

題目描述

小c有n個島嶼。她爲了管理島上的人,決定讓這些島嶼連通。
每個島嶼i有一個概率值pi,和一個友好列表Ai。
小c首先來到了1號島嶼,她會依次執行下面操作:
1、設現在在島嶼x,有px的概率產生一條無向邊連接兩個隨機島嶼,這兩個島嶼不會相同,也不會已經有邊相連。(即在尚不存在的無向邊中隨機一條加入圖中,不會加自環)
2、如果此時所有島嶼連通,她就會心滿意足地離開。
3、否則她會在當前點的友好列表中,隨機選擇一個,走到那座島嶼上。她的不滿意度會+1,並重復第1步。
求小c的期望不滿意度。
題目中的概率都是在模10^9+7域下給出的,答案也需要在模域下求。
即,設答案是a/b,b的逆元是x,你需要輸出ax mod 10^9+7的值。

n^6做法

我們設e(x,i)表示當前在x號點,已經連了i條邊,還要期望經過多少步才能使圖聯通。
我們設q表示i條邊不連通的圖在加入了一條不重複的邊之後聯通的概率。
設dx表示點x的度數。
那麼e(x,i)=px(1q)1dxx>y(e(y,i+1)+1)+(1px)1dxx>y(e(y,i)+1)
我們分層來做,可以注意到如果q能夠求出,每層內可以高斯消元。
那麼複雜度是m*n^3=n^5。
現在問題是這個q怎麼求。
顯然q=i+1(n(n1)/2i)i
我們來考慮怎麼求得分母和分子。

任意條邊聯通圖的橋邊個數和

假設要求m條邊的,現在一條橋邊把圖分成兩個聯通塊,我們不妨枚舉1號點所在聯通塊有k個點,l條邊,那麼貢獻:
Ck1n1k(nk)(kl)(nkml1)
其中組合數表示除了1號點其餘點的編號選取,k和n-k則表示兩邊各要選出一個編號作爲橋邊的端點。
求一個m需要nm的複雜度,要求任意條邊,這裏複雜度是m*nm=n^5。
現在要解決括號裏的問題。

任意個點任意條邊聯通圖個數

設f(n,m)表示n個點m條邊的聯通圖個數。
可以枚舉1號點所在聯通塊的點數和邊數,乘個組合數即可。
複雜度是nmnm=n^6。
因此總複雜度是O(n^6),顯然不能過。

n^5做法

容易發現瓶頸,我們現在用斯特林數容斥來優化任意點任意邊。
原理請參考容斥的原理及廣義應用
我們設dp(i,j)=G is a graph,v(G)=i,e(G)=j(1)c(G)1(c(G)1)!
其中v(G) 表示圖G的點數,e(G) 表示圖G每個聯通塊如果都是完全圖,總邊數是多少。c(G) 表示圖G的聯通塊數。
假如算出了dp,那麼f很好算。
f(n,m)=i>=mdp(n,i)Cmi
我們先考慮如何計算dp,可以設一個g。
g(i,j,k)=G is a graph,v(G)=i,e(G)=j,c(G)=k(1)k1(k1)!
這個g就很好轉移了,可以枚舉1號點所在聯通塊的點數。
複雜度是n*m*n*n=n^5。
然後有了g很好得到dp。
考慮用dp來得到f,可以用n*m*m=n^5時間來得出。
至此,所有部分的複雜度均爲n^5,可惜如果不是常數過分優秀,仍然無法通過。這段代碼後將介紹複雜度更優秀的穩過算法。
本份代碼只能對n<=40在時限內求解。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=50+10,mo=1000000007;
int dp[maxn][maxn*maxn],f[maxn][maxn*maxn],ff[maxn][maxn*maxn],fff[maxn][maxn*maxn],g[maxn*maxn],c[maxn*maxn][maxn*maxn],a[maxn][maxn],b[maxn],d[maxn],p[maxn];
int e[maxn*maxn][maxn],num[maxn];
int h[maxn],go[maxn*maxn],nxt[maxn*maxn],fac[maxn*maxn],inv[maxn*maxn];
int i,j,k,l,r,t,n,m,ans,tot,top,q,qq,x,y,ni,mx;
bool czy;
int qsm(int x,int y){
    if (!y) return 1;
    int t=qsm(x,y/2);
    t=(ll)t*t%mo;
    if (y%2) t=(ll)t*x%mo;
    return t;
}
void add(int x,int y){
    d[x]++;
    go[++tot]=y;
    nxt[tot]=h[x];
    h[x]=tot;
}
int S(int n){
    return n*(n-1)/2;
}
int C(int n,int m){
    if (n<m||m<0) return 0;
    return (ll)fac[n]*inv[m]%mo*inv[n-m]%mo;
}
void gauss(){
    int i,j,k,r,t;
    fo(i,1,n){
        fo(j,i,n)
            if (a[j][i]) break;
        fo(k,i,n) swap(a[i][k],a[j][k]);
        if (!a[i][i]) czy=1;
        fo(j,i+1,n){
            if (!a[j][i]) continue;
            t=(ll)a[j][i]*qsm(a[i][i],mo-2)%mo;
            fo(k,i,n){
                r=(ll)a[i][k]*t%mo;
                //(a[j][k]=(ll)a[i][k]*t%mo-a[j][k])%=mo;
                a[j][k]=r-a[j][k]<0?r-a[j][k]+mo:r-a[j][k];
            }
            b[j]=((ll)b[i]*t%mo-b[j])%mo;
        }
        /*j=i+1;
        while (j+2<=n){
            t=(ll)a[j][i]*qsm(a[i][i],mo-2)%mo;
            fo(k,i,n) (a[j][k]=(ll)a[i][k]*t%mo-a[j][k])%=mo;
            b[j]=((ll)b[i]*t%mo-b[j])%mo;

            t=(ll)a[j+1][i]*qsm(a[i][i],mo-2)%mo;
            fo(k,i,n) (a[j+1][k]=(ll)a[i][k]*t%mo-a[j+1][k])%=mo;
            b[j+1]=((ll)b[i]*t%mo-b[j+1])%mo;

            t=(ll)a[j+2][i]*qsm(a[i][i],mo-2)%mo;
            fo(k,i,n) (a[j+2][k]=(ll)a[i][k]*t%mo-a[j+2][k])%=mo;
            b[j+2]=((ll)b[i]*t%mo-b[j+2])%mo;

            j+=3;
        }
        r=j;
        fo(j,r,n){
            t=(ll)a[j][i]*qsm(a[i][i],mo-2)%mo;
            fo(k,i,n) (a[j][k]=(ll)a[i][k]*t%mo-a[j][k])%=mo;
            b[j]=((ll)b[i]*t%mo-b[j])%mo;
        }*/
    }
    fd(i,n,1){
        fd(j,n,i+1) (b[i]-=(ll)a[i][j]*num[j]%mo)%=mo;
        num[i]=(ll)b[i]*qsm(a[i][i],mo-2)%mo;
        b[i]=0;
    }
}
int main(){
    freopen("island5.in","r",stdin);
    scanf("%d",&n);
    m=n*(n-1)/2;
    mx=max(n,m);
    fo(i,1,n) scanf("%d",&p[i]);
    fo(i,1,n){
        scanf("%d",&t);
        fo(j,1,t){
            scanf("%d",&k);
            add(i,k);
        }
    }
    fac[0]=1;
    fo(i,1,mx) fac[i]=(ll)fac[i-1]*i%mo;
    inv[mx]=qsm(fac[mx],mo-2);
    fd(i,mx-1,0) inv[i]=(ll)inv[i+1]*(i+1)%mo;
    fo(i,1,n) dp[i][S(i)]=1;
    fo(i,0,n)
        fo(j,0,m)
            if (dp[i][j])
                fo(k,1,n-i)
                    if (j+(l=S(k))<=m){
                        t=(ll)dp[i][j]*C(i+k-1,k)%mo;
                        //(dp[i+k][j+S(k)]-=(ll)dp[i][j]*C(i+k-1,k)%mo)%=mo;    
                        dp[i+k][j+l]=dp[i+k][j+l]-t<0?dp[i+k][j+l]-t+mo:dp[i+k][j+l]-t;
                    }
    fo(i,0,n)   
        fo(j,0,m)
            fo(k,j,m){
                t=(ll)dp[i][k]*C(k,j)%mo;
                //(f[i][j]+=(ll)dp[i][k]*C(k,j)%mo)%=mo;
                f[i][j]=f[i][j]+t>=mo?f[i][j]+t-mo:f[i][j]+t;
            }
    fo(i,0,n)
        fo(j,0,m){
            fff[i][j]=ff[i][j]=f[i][j];
            fff[i][j]=(ll)fff[i][j]*i%mo*inv[i]%mo;
            //if (i) ff[i][j]=(ll)ff[i][j]*i%mo*inv[i-1]%mo;
            ff[i][j]=(ll)fff[i][j]*i%mo;
        }
    //return 0;
    fo(j,n-1,m){
        fo(k,1,n-1){
            r=min(j-1,k+j-n);
            fo(l,k-1,r){
                t=(ll)fff[k][l]*ff[n-k][j-l-1]%mo;
                //(g[j]+=(ll)fff[k][l]*ff[n-k][j-l-1]%mo)%=mo;
                g[j]=g[j]+t>=mo?g[j]+t-mo:g[j]+t;
                //(g[j]+=(ll)C(n-1,k)*k%mo*(n-k)%mo*f[k][l]%mo*f[n-k][j-l-1]%mo)%=mo;
            }
        }
        g[j]=(ll)g[j]*fac[n-1]%mo;
    }
    fo(i,1,n) e[i][m]=0;
    //return 0;
    fd(i,m-1,0){
        fo(j,1,n)
            fo(k,j,n) a[j][k]=0;
        //fo(j,1,n) b[j]=0;
        q=(C(m,i)-f[n][i])%mo;
        q=(ll)q*(m-i)%mo;
        q=(ll)g[i+1]*qsm(q,mo-2)%mo;
        (q+=mo)%=mo;
        qq=1-q;
        (qq+=mo)%=mo;
        fo(x,1,n){
            ni=qsm(d[x],mo-2);
            /*(b[x]+=1-p[x])%=mo;
            (b[x]+=mo)%=mo;*/
            t=h[x];
            while (t){
                y=go[t];
                //r=(ll)p[x]*qq%mo*ni%mo*(e[y][i+1]+1)%mo;
                r=e[y][i+1]+1>=mo?e[y][i+1]+1-mo:e[y][i+1]+1;
                //(b[x]+=(ll)p[x]*(1-q)%mo*ni%mo*(e[y][i+1]+1)%mo)%=mo;
                b[x]=b[x]+r>=mo?b[x]+r-mo:b[x]+r;
                (a[x][y]-=(ll)(1-p[x])*ni%mo)%=mo;
                t=nxt[t];
            }
            b[x]=(ll)b[x]*p[x]%mo*qq%mo*ni%mo;
            (b[x]+=1-p[x])%=mo;
            (b[x]+=mo)%=mo;
            (a[x][x]+=1)%=mo;
        }
        fo(j,1,n){
            fo(k,1,n) (a[j][k]+=mo)%=mo;
            (b[j]+=mo)%=mo;
        }
        gauss();
        fo(x,1,n){
            e[x][i]=num[x];
            (e[x][i]+=mo)%=mo;
        }
    }
    ans=e[1][0];
    (ans+=mo)%=mo;
    printf("%d\n",ans);
}

n^4做法

我們可能需要把原來的思路推倒一部分,才能做到如此優秀的複雜度。
現在我們改dp狀態,設e(x,i)表示現在在x號點,還需要i條邊,期望步數是多少。
那麼計算答案是mi=n1e(1,i)P(i使)
考慮計算這個e,寫出轉移:
e(x,i)=1+1dxx>ypxe(y,i1)+(1px)e(y,i)
注意到無論i是多少都是這個轉移式,這啓示我們只需要弄出係數表達,即
e(x,i)=b(x)+ny=1a(x,y)e(y,i1)
我們希望求出a和b,這樣就可以遞推得出e。
a和b很好求,只需要我們一次高斯消元即可,這裏是n^3,然後遞推需要的複雜度是n*m*n=n^4。
因此這個部分只需要n^4。
現在我們需要解決如何求任意條邊恰好使圖聯通的方案數,可以發現P(i使)=P(i使)P(i1使)
因此我們要統計n個點任意邊的聯通圖個數。

n個點任意條邊的聯通圖個數

注意現在不是任意點了,搬上面的做法dp來得到f此時只需要n^4的複雜度。
但是處理dp仍然是一個n^5的東西,考慮優化。
dp(i,j)=G is a graph,v(G)=i,e(G)=j(1)c(G)1(c(G)1)!
觀察這條式子,先想想如何計算一個圖(c(G)1)! 次,我們可以每次枚舉任意一個不包含1號點的聯通塊,因此這個問題迎刃而解。
dp的轉移就是每次枚舉任意一個不包含1號點的聯通塊,複雜度n*m*n=n^4。
於是所有部分均在n^4複雜度內解決。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=50+10,mo=1000000007;
int dp[maxn][maxn*maxn],f[maxn][maxn*maxn],ff[maxn][maxn*maxn],fff[maxn][maxn*maxn],g[maxn*maxn],c[maxn*maxn][maxn*maxn],a[maxn][maxn*2],d[maxn],p[maxn];
int e[maxn][maxn*maxn],num[maxn][maxn];
int h[maxn],go[maxn*maxn],nxt[maxn*maxn],fac[maxn*maxn],inv[maxn*maxn];
int i,j,k,l,r,t,n,m,ans,tot,top,q,qq,x,y,ni,mx;
bool czy;
int qsm(int x,int y){
    if (!y) return 1;
    int t=qsm(x,y/2);
    t=(ll)t*t%mo;
    if (y%2) t=(ll)t*x%mo;
    return t;
}
void add(int x,int y){
    d[x]++;
    go[++tot]=y;
    nxt[tot]=h[x];
    h[x]=tot;
}
int S(int n){
    return n*(n-1)/2;
}
int C(int n,int m){
    if (n<m||m<0) return 0;
    return (ll)fac[n]*inv[m]%mo*inv[n-m]%mo;
}
void gauss(){
    int i,j,k,r,t;
    fo(i,1,n){
        fo(j,i,n)
            if (a[j][i]) break;
        fo(k,i,2*n+1) swap(a[i][k],a[j][k]);
        //if (!a[i][i]) czy=1;
        fo(j,i+1,n){
            if (!a[j][i]) continue;
            t=(ll)a[j][i]*qsm(a[i][i],mo-2)%mo;
            fo(k,i,2*n+1){
                r=(ll)a[i][k]*t%mo;
                //a[j][k]=r-a[j][k]<0?r-a[j][k]+mo:r-a[j][k];
                a[j][k]=(r-a[j][k])%mo;
            }
            //b[j]=((ll)b[i]*t%mo-b[j])%mo;
        }
    }
    fd(i,n,1){
        fd(j,n,i+1) 
            fo(k,n+1,2*n+1) (a[i][k]-=(ll)a[i][j]*num[j][k-n]%mo)%=mo;
        fo(k,1,n+1) num[i][k]=(ll)a[i][k+n]*qsm(a[i][i],mo-2)%mo;
        //b[i]=0;
    }
}
int main(){
    //freopen("island5.in","r",stdin);
    scanf("%d",&n);
    m=n*(n-1)/2;
    mx=max(n,m);
    fo(i,1,n) scanf("%d",&p[i]);
    fo(i,1,n){
        scanf("%d",&t);
        fo(j,1,t){
            scanf("%d",&k);
            add(i,k);
        }
    }
    fac[0]=1;
    fo(i,1,mx) fac[i]=(ll)fac[i-1]*i%mo;
    inv[mx]=qsm(fac[mx],mo-2);
    fd(i,mx-1,0) inv[i]=(ll)inv[i+1]*(i+1)%mo;
    fo(i,1,n) dp[i][S(i)]=1;
    fo(i,0,n)
        fo(j,0,m)
            if (dp[i][j])
                fo(k,1,n-i)
                    if (j+(l=S(k))<=m){
                        t=(ll)dp[i][j]*C(i+k-1,k)%mo;   
                        dp[i+k][j+l]=dp[i+k][j+l]-t<0?dp[i+k][j+l]-t+mo:dp[i+k][j+l]-t;
                    }
    fo(i,n,n)
        fo(j,0,m)
            fo(k,j,m){
                t=(ll)dp[i][k]*C(k,j)%mo;
                f[i][j]=f[i][j]+t>=mo?f[i][j]+t-mo:f[i][j]+t;
            }
    fo(i,1,m) g[i]=(ll)f[n][i]*qsm(C(m,i),mo-2)%mo;
    fd(i,m,1) (g[i]-=g[i-1])%=mo;
    fo(x,1,n){
        a[x][x]++;
        a[x][2*n+1]++;
        ni=qsm(d[x],mo-2);
        t=h[x];
        while (t){
            y=go[t];
            (a[x][y+n]+=(ll)ni*p[x]%mo)%=mo;
            (a[x][y]-=(ll)ni*(1-p[x])%mo)%=mo;
            t=nxt[t];
        }
    }
    fo(i,1,n)
        fo(j,1,2*n+1) (a[i][j]+=mo)%=mo;
    gauss();
    fo(i,1,n) e[i][0]=0;
    fo(i,1,m){
        fo(j,1,n){
            fo(k,1,n) (e[j][i]+=(ll)num[j][k]*e[k][i-1]%mo)%=mo;
            (e[j][i]+=num[j][n+1])%=mo;
        }
    }
    fo(i,1,m) (ans+=(ll)e[1][i]*g[i]%mo)%=mo;
    ans--;
    (ans+=mo)%=mo;
    printf("%d\n",ans);
}
發佈了836 篇原創文章 · 獲贊 317 · 訪問量 59萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章