Tsinsen A1493 城市规划(DP + CDQ分治 + NTT)
题目
Source
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; }