题意:求 n n n个带编号的点能组成多少种无向连通图。
考场上直接在想通项,把自己绕进去了,结果就weila。
正解是一个递推,我们设状态 d p [ i ] dp[i] dp[i]表示 i i i个点的无向连通图的种数。
我们用总方案数减去不合法的方案数来求最终的方案数。首先对于一个i个点的图,一共有 2 i ( i − 1 ) 2 2^\frac{i(i-1)}{2} 22i(i−1)种不同的图。我们固定 1 1 1号节点,枚举和 1 1 1号点连通的连通块大小 j j j,其他 i − j i-j i−j个点一定不和 1 1 1号点连通。
那么与1相连的其它点有 C j − 1 i − 1 C^{i-1}_{j-1} Cj−1i−1中选法, 1 1 1号节点所在联通块有 d p [ j ] dp[j] dp[j]种连法,不与 1 1 1号节点相连的点有 2 ( i − j ) ∗ ( i − j − 1 ) 2 2^{\frac{(i-j)*(i-j-1)}{2}} 22(i−j)∗(i−j−1)种连法。所以我们得到递推式: d p [ i ] = 2 i ( i − 1 ) 2 − ∑ j = 1 i − 1 C j − 1 i − 1 ∗ d p [ j ] ∗ 2 ( i − j ) ∗ ( i − j − 1 ) 2 dp[i]=2^\frac{i(i-1)}{2}-\sum_{j=1}^{i-1}C^{i-1}_{j-1}*dp[j]*2^{\frac{(i-j)*(i-j-1)}{2}} dp[i]=22i(i−1)−j=1∑i−1Cj−1i−1∗dp[j]∗22(i−j)∗(i−j−1)
n 2 n^2 n2预处理组合数求解即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int mod=998244353;
int n;
ll dp[5005],c[5005][5005],num[5005];
ll ksm(ll q,ll w)
{
ll h=1;
while(w)
{
if(w&1)
h=h*q%mod;
q=q*q%mod;
w>>=1;
}
return h;
}
int main()
{
freopen("count.in","r",stdin);
freopen("count.out","w",stdout);
cin>>n;
for(int i=0;i<=5000;++i)
c[i][0]=1;
for(int i=1;i<=5000;++i)
for(int j=1;j<=i;++j)
c[i][j]=(c[i-1][j]%mod+c[i-1][j-1]%mod)%mod;
for(int i=1;i<=n;++i)
num[i]=ksm(2,(i*(i-1))/2);
for(int i=1;i<=n;++i)
{
dp[i]=num[i];
ll sum=0;
for(int j=1;j<=i-1;++j)
{
ll tmp=(c[i-1][j-1]*dp[j]%mod*num[i-j])%mod;
sum=(sum%mod+tmp%mod)%mod;
}
dp[i]=(dp[i]+mod-sum)%mod;
}
cout<<dp[n];
return 0;
}