Luogu P4841 城市规划

题目大意

%   给定 n n n,求有 n n n 个点的简单(无重边无自环)无向连通图的数目。
  输出模 479 ∗ 2 21 + 1 = 1004535809 479*2^{21}+1=1004535809 479221+1=1004535809 意义下的答案。
  数据范围  1 ⩽ n ⩽ 130000 1\leqslant n\leqslant 130000 1n130000

题解

%   题中所给模数的原根为3。
  定义 f n f_n fn 为答案,即简单无向连通图的数量, g n g_n gn 为无向图的数量,显然有 g 0 = g 1 = 1 g_0=g_1=1 g0=g1=1。可以发现,对于任意 n ⩾ 2 n\geqslant2 n2,图中最多有 C n 2 C_n^2 Cn2 条无向边,每条边可有可无,因而有 g n = 2 C n 2 (1) g_n=2^{C_n^2}\tag 1 gn=2Cn2(1)

%   若其中一个点所在的连通图大小为 i i i,则
g n = ∑ i = 1 n C n − 1 i − 1 f i g n − i (2) g_n=\sum_{i=1}^nC_{n-1}^{i-1}f_ig_{n-i}\tag{2} gn=i=1nCn1i1figni(2)

%   把求和拆一下,得
g n = f n + ∑ i = 1 n − 1 C n − 1 i − 1 f i g n − i g_n=f_n+\sum_{i=1}^{n-1}C_{n-1}^{i-1}f_ig_{n-i} gn=fn+i=1n1Cn1i1figni

%   移项,组合数展开
f n = g n − ∑ i = 1 n − 1 ( n − 1 ) ! ( i − 1 ) ! ( n − i ) ! f i g n − i = g n − ( n − 1 ) ! ∑ i = 1 n − 1 f i ( i − 1 ) ! g n − i ( n − i ) ! \begin{aligned} f_n&=g_n-\sum_{i=1}^{n-1}\frac{(n-1)!}{(i-1)!(n-i)!}f_ig_{n-i}\\ &=g_n-(n-1)!\sum_{i=1}^{n-1}\frac{f_i}{(i-1)!}\frac{g_{n-i}}{(n-i)!}\\ \end{aligned} fn=gni=1n1(i1)!(ni)!(n1)!figni=gn(n1)!i=1n1(i1)!fi(ni)!gni

%   考虑
a i = f i ( i − 1 ) ! , b i = g i i ! \begin{aligned} a_i=\frac {f_i}{(i-1)!},b_i=\frac{g_{i}}{i!} \end{aligned} ai=(i1)!fi,bi=i!gi

%   因而 f n = ( n − 1 ) ! a n , g n = i ! b i f_n=(n-1)!a_n,g_n=i!b_i fn=(n1)!an,gn=i!bi,则有
( n − 1 ) ! a n = n ! b n − ( n − 1 ) ! ∑ i = 1 n − 1 a i b n − i (n-1)!a_n=n!b_n-(n-1)!\sum_{i=1}^{n-1}a_ib_{n-i} (n1)!an=n!bn(n1)!i=1n1aibni

%   同除 ( n − 1 ) ! (n-1)! (n1)!,得
a n = n b n − ∑ i = 1 n − 1 a i b n − i a_n=nb_n-\sum_{i=1}^{n-1}a_ib_{n-i} an=nbni=1n1aibni

%   至此便可以用分治FFT处理了,时间复杂度 Θ ( n log ⁡ 2 2 n ) \Theta(n\log_2^2 n) Θ(nlog22n)


%   我们继续考虑 ( 2 ) (2) (2) 式,将 ( 1 ) (1) (1) 式代入 ( 2 ) (2) (2) 式,展开,得
2 C n 2 = ∑ i = 1 n ( n − 1 ) ! ( i − 1 ) ! ( n − i ) ! f i 2 C n − i 2 2^{C_n^2}=\sum_{i=1}^n\frac{(n-1)!}{(i-1)!(n-i)!}f_i2^{C_{n-i}^2} 2Cn2=i=1n(i1)!(ni)!(n1)!fi2Cni2

%   同除 ( n − 1 ) ! (n-1)! (n1)!,得
2 C n 2 ( n − 1 ) ! = ∑ i = 1 n f i ( i − 1 ) ! 2 C n − i 2 ( n − i ) ! \frac{2^{C_n^2}}{(n-1)!}=\sum_{i=1}^n\frac{f_i}{(i-1)!}\frac{2^{C_{n-i}^2}}{(n-i)!} (n1)!2Cn2=i=1n(i1)!fi(ni)!2Cni2

%   可以发现这是卷积的形式,定义
F ( x ) = ∑ i = 1 n f i ( i − 1 ) ! x i G ( x ) = ∑ i = 1 n 2 C i 2 i ! x i H ( x ) = ∑ i = 1 n 2 C i 2 ( i − 1 ) ! x i \begin{aligned} F(x)&=\sum_{i=1}^n\frac{f_i}{(i-1)!}x^i\\ G(x)&=\sum_{i=1}^n\frac{2^{C_{i}^2}}{i!}x^i\\ H(x)&=\sum_{i=1}^n\frac{2^{C_i^2}}{(i-1)!}x^i \end{aligned} F(x)G(x)H(x)=i=1n(i1)!fixi=i=1ni!2Ci2xi=i=1n(i1)!2Ci2xi

%   则有 F ∗ G = H F*G=H FG=H,因而有 F = H ∗ G − 1 F=H*G^{-1} F=HG1,多项式求逆即可求出 F F F,再对答案乘 ( n − 1 ) ! (n-1)! (n1)! 即可。时间复杂度为 Θ ( n log ⁡ 2 n ) \Theta(n\log_2 n) Θ(nlog2n)

代码

分治FFT?不存在的。

#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define MAXN 460010
const long long mod=1004535809,G=3,inG=334845270;
inline long long pows(long long a,long long b){
	long long ret=1;
	while(b){
		if(b&1) ret=ret*a%mod;
		b>>=1;a=a*a%mod;
	} return ret;
}
#define invt(x) pows(x,mod-2)
class poly{
	public:
	static const int maxn=MAXN;
	long long *t;
	int limit,l,inmit;
	inline long long& operator[](int x){return t[x];}
	inline const long long& operator[](int x)const{return t[x];}
	inline poly(int n=0):t(NULL),limit(1),l(0){init(n);}
	inline void init(int n){
		if(limit>=n) return;
		if(t) free(t);
		while(limit<n) limit<<=1,++l;
		inmit=invt(limit);
		t=(long long*)calloc(limit+5,sizeof(long long));
	}
	void fnt(int type){
		static long long r[MAXN];
		static int now=0;
		if(now!=limit&&(now=limit))
			for(int i=1;i<limit;i++)
				r[i]=((r[i>>1]>>1)|((i&1)<<(l-1)));
		for(int i=1;i<limit;i++)
			if(i<r[i]) swap(t[i],t[r[i]]);
		for(int mid=1;mid<limit;mid<<=1){
			long long wn=pows(type==1?G:inG,(mod-1)/(mid<<1));
			for(int j=0,B=(mid<<1);j<limit;j+=B){
				long long w=1;
				for(int k=0;k<mid;k++,w=w*wn%mod){
					int x=t[j+k],y=w*t[j+k+mid]%mod;
					t[j+k]=(x+y)%mod;t[j+k+mid]=(x-y+mod)%mod;
				}
			}
		} if(type==-1) for(int i=0;i<limit;i++)
			t[i]=t[i]*inmit%mod;
	}
	inline void inv(int n,poly &g)const{
		g.init(n+n);
		if(n==1) return(void)(g[0]=invt(t[0]));
		inv(n+1>>1,g);
		poly F(n+n),G(n+n);
		for(int i=0;i<n;i++) F[i]=t[i];
		for(int i=0;i<n+1>>1;i++) G[i]=g[i];
		F.fnt(1);G.fnt(1);
		for(int i=0;i<F.limit;i++)
			F[i]=G[i]*((2-F[i]*G[i]%mod+mod)%mod)%mod;
		F.fnt(-1);
		for(int i=0;i<n;i++) g[i]=F[i];
	}
};
int n;
long long frac[MAXN],ifrac[MAXN];
int main(){
	scanf("%d",&n);++n;
	frac[0]=1;
	for(int i=1;i<=n;i++)
		frac[i]=frac[i-1]*i%mod;
	ifrac[n]=invt(frac[n]);
	for(int i=n-1;i>=0;i--)
		ifrac[i]=ifrac[i+1]*(i+1)%mod;
	poly G(n+n),H(n+n),inv_G(n+n);
	for(int i=1;i<n;i++)
		G[i]=ifrac[i]*pows(2,((long long)i*(i-1)/2))%mod;
	for(int i=1;i<n;i++)
		H[i]=ifrac[i-1]*pows(2,((long long)i*(i-1)/2))%mod;
	G[0]=1;G.inv(n,inv_G);
	inv_G.fnt(1);H.fnt(1);
	for(int i=0;i<H.limit;i++)
		H[i]=inv_G[i]*H[i]%mod;
	H.fnt(-1);
	printf("%lld\n",H[n-1]*frac[n-2]%mod);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值