【XSY1538】连在一起的幻想乡 数学 无向连通图计数

题目大意

​  给你\(n,p\),求\(n\)个点组成的所有无向连通图的边数的平方和模\(p\)

​  \(n\leq 2000,p\leq {10}^9\)

题解

​  设\(m=\frac{n(n-1)}{2},h0_n=n\)个点无向图的个数,\(h1_n=n\)个点组成的所有无向图的边数之和,\(h2_n=n\)个点组成的所有无向图的边数的平方和,\(f0_n=n\)个点无向连通图的个数,\(f1_n=n\)个点组成的所有无向连通图的边数之和,\(f2_n=n\)个点组成的所有无向连通图的边数的平方和

  每条边可以选或不选,所以

\[h0_n=2^m \]

  因为每条边会被选中\(2^{m-1}\)次,所以

\[h1_n=m2^{m-1} \]

  因为选\(i\)条边有\(\binom{m}{i}\)种方法,所以

\[h2_n=\sum_{i=0}^mi^2(^m_{~i}) \]

  但是这个式子没法快速算出来。我们换一种思路。

​  我们枚举点\(1\)连出去的边的条数\(i\),根据\({(x+\sum y)}^2=x^2\sum1+2x\sum y+\sum y^2\),可得

\[h2_n=\sum_{i=0}^{n-1}\binom{n-1}{i}(h2_{n-1}+2ih1_{n-1}+i^2h0_{n-1}) \]

​  我们枚举与点\(1\)在同一个联通分量里的点的个数,计算联通分量数\(\geq2\)的答案,然后用总的答案去减

\[f0_n=h0_n-\sum_{i=1}^{n-1}\binom{n-1}{i-1}f0_ih0_{n-i} \]

\[f1_n=h1_n-\sum_{i=1}^{n-1}\binom{n-1}{i-1}(f0_ih1_{n-i}+f1_ih0_{n-i}) \]

\[f2_n=h2_n-\sum_{i=1}^{n-1}\binom{n-1}{i-1}(f0_ih2_{n-i}+2f1_ih1_{n-i}+f2_ih0_{n-i}) \]

​  时间复杂度:\(O(n^2)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
ll p;
ll fp(ll a,ll b)
{
	ll s=1;
	while(b)
	{
		if(b&1)
			s=s*a%p;
		a=a*a%p;
		b>>=1;
	}
	return s;
}
ll c[2010][2010];
ll f0[2010],f1[2010],f2[2010];
ll h0[2010],h1[2010],h2[2010];
int main()
{
//	freopen("road.in","r",stdin);
	int n;
	scanf("%d%lld",&n,&p);
	int i,j;
	c[0][0]=1;
	for(i=1;i<=n;i++)
	{
		c[i][0]=1;
		for(j=1;j<=i;j++)
			c[i][j]=(c[i-1][j]+c[i-1][j-1])%p;
	}
	h0[1]=1;
	h1[1]=h2[1]=0;
	for(i=2;i<=n;i++)
	{
		int m=i*(i-1)/2;
		h0[i]=fp(2,m);
		h1[i]=m*fp(2,m-1)%p;
		h2[i]=0;
		for(j=0;j<=i-1;j++)
			h2[i]=(h2[i]+c[i-1][j]*((h2[i-1]+2*j%p*h1[i-1]%p+j*j%p*h0[i-1]%p)%p)%p)%p;
	}
	f0[0]=f0[1]=1;
	for(i=2;i<=n;i++)
	{
		f0[i]=h0[i];
		f1[i]=h1[i];
		f2[i]=h2[i];
		for(j=1;j<=i-1;j++)
		{
			f0[i]=(f0[i]-c[i-1][j-1]*f0[j]%p*h0[i-j]%p)%p;
			f1[i]=(f1[i]-c[i-1][j-1]*((f0[j]*h1[i-j]%p+f1[j]*h0[i-j]%p)%p)%p)%p;
			f2[i]=(f2[i]-c[i-1][j-1]*((f0[j]*h2[i-j]%p+2*f1[j]*h1[i-j]%p+f2[j]*h0[i-j]%p)%p)%p)%p;
		}
	}
	ll ans=(f2[n]%p+p)%p;
	printf("%lld\n",ans);
	return 0;
}
posted @ 2018-03-05 20:32  ywwyww  阅读(413)  评论(0编辑  收藏  举报