首页 > 代码库 > Tsinsen A1493 城市规划(DP + CDQ分治 + NTT)

Tsinsen A1493 城市规划(DP + CDQ分治 + NTT)

题目

Source

http://www.tsinsen.com/A1493

Description

刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了.
刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通.
为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案.
好了, 这就是困扰阿狸的问题. 换句话说, 你需要求出n个点的简单(无重边无自环)无向连通图数目.
由于这个数字可能非常大, 你只需要输出方案数mod 1004535809(479 * 2 ^ 21 + 1)即可.

Input

仅一行一个整数n(<=130000)

Output

仅一行一个整数, 为方案数 mod 1004535809.

Sample Input

3
4
100000

Sample Output

4
38
829847355

 

分析

楼教主男人八题也又一题是和这题一样,n个有标号点能构成的简单无向连通图数,不过这题题目数据大很多。做法如下:

  • 补集转化,设$f[n]$表示n个带标号的点能构成的简单图数,而只要利用$f[n]$减去不连通的数目就是要求的答案了。其中$f[n]=2^{n(n-1)/2}$,因为完全图有$n(n-1)/2$条边,对于每条边选或不选来构成一张简单图。
  • $dp[n]$表示n个带标号的点能构成的简单无向连通图数
  • 考虑通过固定一个点,枚举这个点所在连通块的大小来转移:$dp[n]\ =\ f[n]-\sum_{i=1}^{n-1}C_{n-1}^{i-1}dp[i]f[n-i]$
  • 整理成卷积形式:$d[n]\ =\ f[n]-(n-1)!\sum_{i=1}^{n-1}((i-1)!)^{-1}d[i] \times ((n-i)!)^{-1}f[n-i]$
  • 最后就是用分治FFT来做了。

 

代码

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define MAXN 262144//const long long P=50000000001507329LL; // 190734863287 * 2 ^ 18 + 1const int P=1004535809; // 479 * 2 ^ 21 + 1//const int P=998244353; // 119 * 2 ^ 23 + 1const int G=3;long long mul(long long x,long long y){	return x*y%P;}long long qpow(long long x,long long k){	long long ret=1;	while(k){		if(k&1) ret=mul(ret,x);		k>>=1;		x=mul(x,x);	}	return ret;}long long wn[25];void getwn(){	for(int i=1; i<=21; ++i){		int t=1<<i;		wn[i]=qpow(G,(P-1)/t);	}}int len;void NTT(long long y[],int op){	for(int i=1,j=len>>1,k; i<len-1; ++i){		if(i<j) swap(y[i],y[j]);		k=len>>1;		while(j>=k){			j-=k;			k>>=1;		}		if(j<k) j+=k;	}	int id=0;	for(int h=2; h<=len; h<<=1) {		++id;		for(int i=0; i<len; i+=h){			long long w=1;			for(int j=i; j<i+(h>>1); ++j){				long long u=y[j],t=mul(y[j+h/2],w);				y[j]=u+t;				if(y[j]>=P) y[j]-=P;				y[j+h/2]=u-t+P;				if(y[j+h/2]>=P) y[j+h/2]-=P;				w=mul(w,wn[id]);			}		}    }    if(op==-1){		for(int i=1; i<len/2; ++i) swap(y[i],y[len-i]);		long long inv=qpow(len,P-2);		for(int i=0; i<len; ++i) y[i]=mul(y[i],inv);    }}void Convolution(long long A[],long long B[],int n){	for(len=1; len<(n<<1); len<<=1);	for(int i=n; i<len; ++i){		A[i]=B[i]=0;	}	NTT(A,1); NTT(B,1);	for(int i=0; i<len; ++i){		A[i]=mul(A[i],B[i]);	}	NTT(A,-1);}long long A[MAXN],B[MAXN];long long d[MAXN],f[MAXN]={1},fact[MAXN]={1},fact_inv[MAXN]={1};/*    d[n] = f[n]-fact[n-1]*Σ(fact_inv[i-1]*d[i]*f[n-i]*fact_inv[n-i])*/void cdq(int l,int r){    if(l==r) return;    int mid=l+r>>1;    cdq(l,mid);    for(int i=l; i<=mid; ++i){        A[i-l]=mul(fact_inv[i-1],d[i]);    }    for(int i=mid-l+1; i<=r-l; ++i) A[i]=0;    for(int i=0; i<=r-l; ++i){        B[i]=mul(fact_inv[i],f[i]);    }    Convolution(A,B,r-l+1);    for(int i=mid+1; i<=r; ++i){        d[i]-=mul(fact[i-1],A[i-l]);        if(d[i]<0) d[i]+=P;    }    cdq(mid+1,r);}int main(){	getwn();	for(int i=1; i<=130000; ++i){        fact[i]=mul(fact[i-1],i);        fact_inv[i]=qpow(fact[i],P-2);        d[i]=f[i]=qpow(2,(i-1LL)*i/2);	}	cdq(1,130000);	int n;	while(~scanf("%d",&n)){        printf("%lld\n",d[n]);	}	return 0;}

 

Tsinsen A1493 城市规划(DP + CDQ分治 + NTT)