7010. 2021.03.13【2021省赛模拟】graph
\(n\)个点的图,每次随机两个点\(u,v\)(两者独立随机互不相干),连边\((u,v)\),问第一次联通的期望步数。
\(n\le 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=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\n",ans);
return 0;
}