前言
感覺這題挺棒棒的。
題目描述
小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的度數。
那麼
我們分層來做,可以注意到如果q能夠求出,每層內可以高斯消元。
那麼複雜度是m*n^3=n^5。
現在問題是這個q怎麼求。
顯然
我們來考慮怎麼求得分母和分子。
任意條邊聯通圖的橋邊個數和
假設要求m條邊的,現在一條橋邊把圖分成兩個聯通塊,我們不妨枚舉1號點所在聯通塊有k個點,l條邊,那麼貢獻:
其中組合數表示除了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,那麼f很好算。
我們先考慮如何計算dp,可以設一個g。
這個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條邊,期望步數是多少。
那麼計算答案是
考慮計算這個e,寫出轉移:
注意到無論i是多少都是這個轉移式,這啓示我們只需要弄出係數表達,即
我們希望求出a和b,這樣就可以遞推得出e。
a和b很好求,只需要我們一次高斯消元即可,這裏是n^3,然後遞推需要的複雜度是n*m*n=n^4。
因此這個部分只需要n^4。
現在我們需要解決如何求任意條邊恰好使圖聯通的方案數,可以發現
因此我們要統計n個點任意邊的聯通圖個數。
n個點任意條邊的聯通圖個數
注意現在不是任意點了,搬上面的做法dp來得到f此時只需要n^4的複雜度。
但是處理dp仍然是一個n^5的東西,考慮優化。
觀察這條式子,先想想如何計算一個圖
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);
}