(n)个点的图,每次随机两个点(u,v)(两者独立随机互不相干),连边((u,v)),问第一次联通的期望步数。
(nle 100)
讲阳间的(O(n^5))做法。
设(G_i)表示保持在恰好(i)个联通块的期望时间,(F_i)为至少。求(F_i)之后容斥得到(G_i)。
考虑把点划分成若干个块,保证块之间不连通,块内的点可以联通可以不联通。
如果求出了这个东西的答案,则因为(F_n=sum_{i=n}^N S(N,i)G_i),斯特林反演得(G_n=sum_{i=n}^N s(N,i)F_i(-1)^{N-i})。
对于一个划分的方案,贡献为(frac{1}{1-frac{sum a_i^2}{n^2}}),其中(a_i)表示块大小。(可以看做后面的连边都要限制在每个块内,不发生块的合并,保持在这状态的期望步数)。
可以dp计算:设(dp_{i,j,k})表示用了(i)个点形成了(j)个块,(sum a_i^2=k)的贡献(方案数,这个方案数和概率无关,它是用来处理“至少”的)。然后(F_j=sum dp_{n,j,k}frac{1}{1-frac{k}{n^2}})。
这个dp可以(O(n^5))算。因为有值的地方不会很多,所以时间约等于(O(n^4))。
题解做法:
设(dp_{n,k})为(n)个点进行了(k)步不连通的概率。
据此列出DP方程:
[dp_{n,k}=sum_{i=1}^{n-1}sum_{j=0}^kinom{n-1}{i-1}(1-dp_{i,j})inom{k}{j}(frac{i}{n})^{2j}(frac{n-i}{n})^{2(k-j)}
]
然后它发现可以表示成(dp_{n,k}=sum_{i=0}^{n^2-1}c_{n,i}(frac{i}{n^2})^k)的形式,可以归纳证明。
讲真我觉如果是我,我肯定不会发现的。所以扩展性不太好。
根据DP方程得到(c)的递推式,算出(c)就好了,时间(O(n^4))。
using namespace std;
#include <bits/stdc++.h>
#define N 105
#define ll long long
int n,mo;
ll qpow(ll x,ll y=mo-2){
ll r=1;
for (;y;y>>=1,x=x*x%mo)
if (y&1)
r=r*x%mo;
return r;
}
int dp[N][N*N];
int s[N][N],C[N][N];
ll f[N],g[N];
int main(){
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
// freopen("in.txt","r",stdin);
scanf("%d%d",&n,&mo);
C[0][0]=1;
for (int i=1;i<=n;++i){
C[i][0]=1;
for (int j=1;j<=i;++j)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mo;
}
s[0][0]=1;
for (int i=1;i<=n;++i)
for (int j=1;j<=i;++j)
s[i][j]=(s[i-1][j-1]+(ll)s[i-1][j]*(i-1))%mo;
dp[0][0]=1;
for (int j=0;j<n;++j){
static int _dp[N][N*N];
memset(_dp,0,sizeof _dp);
for (int i=j;i<n;++i)
for (int k=0;k<=i*i;++k)
if (dp[i][k]){
int tmp=dp[i][k];
for (int t=1;i+t<=n;++t)
_dp[i+t][k+t*t]=(_dp[i+t][k+t*t]+(ll)tmp*C[i+t-1][t-1])%mo;
}
memcpy(dp,_dp,sizeof _dp);
if (j+1>=2){
for (int k=1;k<n*n;++k)
if (dp[n][k])
(f[j+1]+=(ll)dp[n][k]*n*n%mo*qpow(n*n-k))%=mo;
}
}
for (int i=2;i<=n;++i)
for (int j=i;j<=n;++j)
(g[i]+=s[j][i]*f[j]%mo*(j-i&1?-1:1))%=mo;
ll ans=0;
for (int i=2;i<=n;++i)
ans+=g[i];
ans=(ans%mo+mo)%mo;
printf("%lld
",ans);
return 0;
}