[BZOJ3456]城市规划

Description

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


Input

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


Output

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


Solution

在这里插入图片描述

在这里插入图片描述


Code

#include<bits/stdc++.h>
using namespace std; 
typedef long long ll;
const int p=1004535809;
int a[300010],b[300010],c[300010],d[300010];
int rev[300010],pg[300010],pinvg[300010],n;
int fac[300010]={1},ifac[300010];
int qpow(int x,ll y){
	int s=1,bas=x;
	while(y){
		if(y&1) s=(s*1ll*bas)%p;
		bas=(bas*1ll*bas)%p;
		y>>=1;
	}
	return s;
}
void NTT(int *A,int limit,int typ){
	for(int i=0;i<limit;i++)
	if(i<rev[i]) swap(A[i],A[rev[i]]);
	for(int i=1;i<limit;i<<=1){
		int wn=typ>0?pg[i<<1]:pinvg[i<<1];
		for(int j=0,s=i<<1;j<limit;j+=s)
		for(int k=0,w=1;k<i;k++,w=(w*1ll*wn)%p){
			int x=A[j+k],y=(A[j+k+i]*1ll*w)%p;
			A[j+k]=(x+y)%p,A[j+k+i]=(x-y+p)%p;
		}
	}
	if(typ<0){
		int inv=qpow(limit,p-2);
		for(int i=0;i<limit;i++) 
		A[i]=(A[i]*1ll*inv)%p;
	}
}
void getinv(int *A,int *B,int l){
	if(l==1){
		B[0]=qpow(A[0],p-2);
		return;
	}
	getinv(A,B,(l+1)>>1);
	int limit=1,bit=0;
	while(limit<2*l)
	limit<<=1,bit++;
	for(int i=0;i<limit;i++)
	rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
	for(int i=0;i<l;i++) c[i]=A[i];
	for(int i=l;i<limit;i++) c[i]=0;
	NTT(c,limit,1),NTT(B,limit,1);
	for(register int i=0;i<limit;i++)
	B[i]=(2ll-c[i]*1ll*B[i]%p+p)%p*1ll*B[i]%p; NTT(B,limit,-1);
	for(int i=l;i<limit;i++) B[i]=0; 
}
int invg;
void init(int limit){
	invg=qpow(3,p-2);
    for(int i=1;i<=limit;i<<=1) 
	pg[i]=qpow(3,(p-1)/i);//,cout<<p3[i]<<endl;
    for(int i=1;i<=limit;i<<=1) 
	pinvg[i]=qpow(invg,(p-1)/i);
	for(int i=1;i<=n;i++)
	fac[i]=(fac[i-1]*1ll*i)%p;
	ifac[n]=qpow(fac[n],p-2);
	for(int i=n;i;i--)
	ifac[i-1]=(ifac[i]*1ll*i)%p;
}
int main(){
	a[0]=1;
	scanf("%d",&n);
	int limit=1,bit=0;
	while(limit<2*(n+1)) 
	limit<<=1,bit++; 
	init(limit);
	for(int i=1;i<=n;i++){
		int x=qpow(2,i*1ll*(i-1)/2);
		//cout<<x<<" "<<ifac[i]<<endl;
		a[i]=(x*1ll*ifac[i])%p;
		d[i]=(x*1ll*ifac[i-1])%p;
	}
	getinv(a,b,n+1);
	for(int i=0;i<limit;i++)
	rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
	NTT(b,limit,1),NTT(d,limit,1);
	for(int i=0;i<limit;i++)
	b[i]=(b[i]*1ll*d[i])%p;
	NTT(b,limit,-1);
	printf("%lld",(b[n]*1ll*fac[n-1])%p);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值