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 + 1
const int P=1004535809; // 479 * 2 ^ 21 + 1
//const int P=998244353; // 119 * 2 ^ 23 + 1
const 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;
}

 

posted @ 2016-10-03 21:57  WABoss  阅读(570)  评论(0编辑  收藏  举报