【BZOJ3456】城市规划(生成函数,多项式运算)

【BZOJ3456】城市规划(生成函数,多项式运算)

题面

\(n\)个点的无向连通图个数。

\(n<=130000\)

题解

\(n\)个点的无向图的个数\(g(n)=2^{C_n^2}\)。设\(n\)个点的无向连通图个数为\(f(n)\)。有等式:

\[g(n)=\sum_{i=1}^{n}C_{n-1}^{i-1}f(i)g(n-i) \]

即考虑枚举\(1\)号点所在联通块的点。

\(g(n)\)带入式子

\[2^{C_n^2}=\sum_{i=1}^{n}C_{n-1}^{i-1}f(i)2^{C_{n-i}^2} \]

将组合数拆开后,两侧除掉只与\(n\)有关的\((n-1)!\)

\[\frac{2^{C_n^2}}{(n-1)!}=\sum_{i=1}^{n}\frac{f(i)}{(i-1)!}\frac{2^{C_{n-i}^2}}{(n-i)!} \]

构造生成函数\(F(x)=\sum_{i=1}^{\infty}\frac{f(i)}{(i-1)!}x^i\),\(G(x)=\sum_{i=0}^{\infty}\frac{g(i)}{i!}x^i\),\(C(x)=\sum_{i=1}^{\infty}\frac{g(i)}{(i-1)!}x^i\)

然后有式子\(C(x)=F(x)G(x)\)\(F(x)=C(x)G^{-1}(x)\)

多项式求逆即可,时间复杂度\(O(nlogn)\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 333333
const int MOD=1004535809;
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int n,inv[MAX],jc[MAX],jv[MAX],p[MAX];
int fpow(int a,int b)
{
	int s=1;
	while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}
	return s;
}
int r[MAX],W[MAX];
void NTT(int *P,int len,int opt)
{
	int N,l=0;for(N=1;N<len;N<<=1)++l;
	for(int i=1;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
	for(int i=1;i<N;++i)if(i<r[i])swap(P[i],P[r[i]]);
	for(int i=1;i<N;i<<=1)
	{
		int w=fpow(3,(MOD-1)/(i<<1));W[0]=1;
		for(int k=1;k<i;++k)W[k]=1ll*W[k-1]*w%MOD;
		for(int p=i<<1,j=0;j<N;j+=p)
			for(int k=0;k<i;++k)
			{
				int X=P[j+k],Y=1ll*W[k]*P[i+j+k]%MOD;
				P[j+k]=(X+Y)%MOD;P[i+j+k]=(X+MOD-Y)%MOD;
			}
	}
	if(opt==-1)
	{
		reverse(&P[1],&P[N]);
		for(int i=0,inv=fpow(N,MOD-2);i<N;++i)P[i]=1ll*P[i]*inv%MOD;
	}
}
int A[MAX],B[MAX];
void Inv(int *a,int *b,int len)
{
	if(len==1){b[0]=fpow(a[0],MOD-2);return;}
	Inv(a,b,len>>1);
	for(int i=0;i<len;++i)A[i]=a[i],B[i]=b[i];
	NTT(A,len<<1,1);NTT(B,len<<1,1);
	for(int i=0;i<(len<<1);++i)A[i]=1ll*A[i]*B[i]%MOD*B[i]%MOD;
	NTT(A,len<<1,-1);
	for(int i=0;i<len;++i)b[i]=((b[i]+b[i])%MOD+MOD-A[i])%MOD;
	for(int i=0;i<(len<<1);++i)A[i]=B[i]=0;
}
int C[MAX],G[MAX],D[MAX],F[MAX];
int main()
{
	n=read();
	int N;for(N=1;N<=n;N<<=1);
	inv[0]=jc[0]=inv[1]=jc[1]=jv[0]=jv[1]=1;
	for(int i=2;i<=n;++i)
	{
		jc[i]=1ll*jc[i-1]*i%MOD;
		inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
		jv[i]=1ll*jv[i-1]*inv[i]%MOD;
	}
	p[0]=p[1]=1;
	for(int i=2;i<=n;++i)p[i]=fpow(2,1ll*i*(i-1)/2%(MOD-1));
	for(int i=0;i<=n;++i)G[i]=1ll*p[i]*jv[i]%MOD;
	for(int i=1;i<=n;++i)C[i]=1ll*p[i]*jv[i-1]%MOD;
	Inv(G,D,N);
	NTT(D,N,1);NTT(C,N,1);
	for(int i=0;i<N;++i)F[i]=1ll*D[i]*C[i]%MOD;
	NTT(F,N,-1);
	printf("%lld\n",1ll*F[n]*jc[n-1]%MOD);
	return 0;
}

posted @ 2018-04-11 20:17  小蒟蒻yyb  阅读(472)  评论(0编辑  收藏  举报