Jzoj3303 城市规划

刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了.

刚才说过, 阿狸的国家有n 个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通.

为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案.

好了, 这就是困扰阿狸的问题. 换句话说, 你需要求出n 个点的简单(无重边无自环)无向连通图数目.

由于这个数字可能非常大, 你只需要输出方案数mod 1004535809(479 * 2 ^21 + 1)即可.

NTT第一道例题

我们令f[k]表示n个点的图的无向图个数,显然f[k]=2^C(k,2)

令g[k]表示k个点的无向连通图个数,用dp计算就得到g[k]=f[k]-ΣC(k-1,i-1)*g[i]*f[k-i] (1<=i<k)

g[k]=f[k]-(k-1)!Σg[i]/(i-1)!*f[k-i]/(k-i)!

g[k]=f[k]-(k-1)!Σg'[i]*f'[k-i]

发现不能直接做,我们考虑用分治,计算g[l..m]对g[m+1,r]的贡献,这样一次就是O(n lg n)的

总复杂度O(n lg^2 n)

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 280010
#define Rg register
#define LL long long
#define M 1004535809
using namespace std;
int n,nL,m,a[N],b[N],z[N];
LL f[N],g[N],js[N],inv[N],W[N],iW[N];
inline int p2(int x,int t=1){
	for(;t<x;t<<=1); return t<<1;
}
inline int lg(int x,int t=0){
	for(;x>1;x>>=1) ++t; return t;
}
inline int pow(LL x,LL k,LL& s){
	for(s=1;k;x=x*x%M,k>>=1) k&1?s=s*x%M:0;
}
void NTT(int* a,int* b,int n,int g){
	for(int i=0,j,k,t;i<n;++i){
		for(j=0,k=i,t=n-1;t;t>>=1,k>>=1) j=(j<<1)|(k&1);
		b[j]=a[i];
	}
	for(int m=2;m<=n;m<<=1){
		Rg LL w=g?W[m]:iW[m],u,v,z;
		for(Rg int i,k=m>>1,j=0;j<n;j+=m)
			for(z=1,i=0;i<k;++i,z=z*w%M){
				u=b[i+j]; v=z*b[i+j+k]%M;
				b[i+j]=(u+v)%M; b[i+j+k]=(u-v+M)%M;
			}
	}
	memcpy(a,b,n<<2);
}
void MUL(int l,int r,int m){
	LL iv; int n=p2(m-l+1);
	memset(a,0,n<<2);
	memset(b,0,n<<2);
	for(int i=1;i<=r-l+1;++i)
		a[i]=g[i+l-1]*inv[i+l-2]%M;
	for(int i=1;i<=m-l;++i)
		b[i]=f[i]*inv[i]%M;
	NTT(a,z,n,1); NTT(b,z,n,1);
	for(int i=0;i<n;++i) a[i]=(LL)a[i]*b[i]%M;
	NTT(a,z,n,0); pow(n,M-2,iv);
	for(int i=0;i<n;++i) a[i]=(LL)a[i]*iv%M;
}
void cdq(int l,int r){
	if(l==r) g[l]=(f[l]-g[l]*js[l-1]%M+M)%M;
	else{
		int m=l+r>>1;
		cdq(l,m);
		MUL(l,m,r);
		for(int i=m+1;i<=r;++i) g[i]=(g[i]+a[i-l+1])%M;
		cdq(m+1,r);
	}
}
int main(){
	scanf("%d",&n);
	for(int i=*js=*inv=1;i<=n;++i) js[i]=js[i-1]*i%M;
	pow(js[n],M-2,inv[n]); for(int i=n;i;--i) inv[i-1]=inv[i]*i%M;
	for(int i=1;i<=n;++i) pow(2,i*(i-1ll)>>1,f[i]);
	for(int j=1;j<=N;j<<=1){
		pow(3,(M-1)/j,W[j]); pow(W[j],M-2,iW[j]);
	}
	cdq(1,p2(n)>>1); 
	printf("%lld\n",g[n]);
}

posted @ 2018-01-25 08:05  扩展的灰(Extended_Ash)  阅读(100)  评论(0编辑  收藏  举报