[集训队作业2013]城市规划

庆祝本蒟蒻靠自己A掉的第一道黑题

前置知识:

分治NTT
不会的可以看这个

题目大意:

询问n个点(有标号)构成的无向连通图的总方案数

解法

考虑1号节点所在联通块大小,设\(F[i]\)表示\(i\)个点构成无向连通图的总方案数
正难则反:n个点构成无向图总数为\(2^{\frac{n*(n-1)}{2}}\),考虑1所在连通块大小,不难得出:

\[F[i]=2^{\frac{i*(i-1)}{2}}-\sum_{j=1}^{i-1}f[j]\times \mathrm{C}_{i-1}^{j-1}\times 2^{\frac{(i-j)*(i-j-1)}{2}} \]

算这个式子应该是\(O(n^2)\)的,考虑优化
把组合数式子拆开,得:

\[F[i]=2^{\frac{i*(i-1)}{2}}-\sum_{j=1}^{i-1}F[j]\times \frac{(i-1)!}{(j-1)!(i-j)!}\times 2^{\frac{(i-j)*(i-j-1)}{2}} \\ \therefore \frac{F[i]}{(i-1)!}=\frac{2^{\frac{i*(i-1)}{2}}}{(i-1)!}-\sum_{j=1}^{i-1}\frac{F[j]}{(j-1)!} \times \frac{2^{\frac{(i-j)*(i-j-1)}{2}}}{(i-j)!} \]

\(f_i=\frac{F[i]}{(i-1)!}\),\(g_i=\frac{2^{\frac{i*(i-1)}{2}}}{i!}\)

\[\therefore f_i=g_i\times \frac{i!}{(i-1)!}-\sum_{j=1}^{i-1}f_j\times g_{i-j} \]

这样就像卷积的形式了
分治ntt即可

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+8,Mod=1004535809,G=3,IG=(Mod+1)/G;
inline int read() {
	int s=1,a=0;
	char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') s=-s;
		c=getchar();
	}
	while(isdigit(c)) {
		a=a*10+c-'0';
		c=getchar();
	}
	return s*a;
}
int n,f[N],g[N],re[N],F[N],pw2[N],jc[N],jcinv[N],c[N],T[N];
int ksm(int a,int b) {
	int c=1;
	while(b) {
		if(b&1) {
			c*=a;
			c%=Mod;
		}
		a*=a;
		a%=Mod;
		b>>=1;
	}
	return c;
}
int mult(int a,int b) {
	return a*b%Mod;
}
void pre() {
	jc[0]=1;
	for(int i=1;i<=n;i++) {
		jc[i]=mult(jc[i-1],i)%Mod;
	}
	for(int i=1;i<=n;i++) {
		c[i]=((i*(i-1))/2)%(Mod-1);
	}
	jcinv[0]=1;
	for(int i=1;i<=n;i++) {
		jcinv[i]=ksm(jc[i],Mod-2);
	}
	F[0]=0;
	for(int i=1;i<=n;i++) {
		F[i]=ksm(2,c[i])*jcinv[i-1]%Mod;
	}
	T[0]=0;
	for(int i=1;i<=n;i++) {
		T[i]=mult(ksm(2,c[i]),jcinv[i]);
	}
}
void ntt(int *arr,int len,int op) {
	for(int i=0;i<len;i++) {
		if(i<re[i]) {
			swap(arr[i],arr[re[i]]);
		}
	}
	for(int i=2;i<=len;i<<=1) {
		int wn=ksm(op==1?G:IG,(Mod-1)/i);
		for(int j=0;j<len;j+=i) {
			int wk=1;
			for(int k=j;k<j+(i>>1);k++,wk=wk*wn%Mod) {
				int xx=arr[k],yy=arr[k+(i>>1)]*wk%Mod;
				arr[k]=(xx+yy)%Mod;
				arr[k+(i>>1)]=(xx-yy+Mod)%Mod;
			}
		}
	}
}
void cdq(int l,int r,int tim) {
	if(l+1>=r)
		return;
	int mid=(l+r)>>1;
	cdq(l,mid,tim-1);
	int len=r-l;
	for(int i=0;i<len;i++)
		re[i]=(re[i>>1]>>1)|((i&1)<<(tim-1));
	for(int i=l;i<mid;i++) f[i-l]=F[i];
	for(int i=mid;i<r;i++) f[i-l]=0;
	for(int i=0;i<len;i++) g[i]=T[i];
//	cout<<l<<" "<<f[1]<<endl;
	ntt(f,len,1),ntt(g,len,1);
	for(int i=0;i<len;i++) f[i]=f[i]*g[i]%Mod;
	ntt(f,len,-1);
	int inv=ksm(len,Mod-2);
	for(int i=mid;i<r;i++) F[i]=(F[i]-f[i-l]*inv%Mod+Mod)%Mod;
	cdq(mid,r,tim-1);
}
signed main() {
	n=read();
	pre();
	int lens=1,tim=0;
	while(lens<=(n)) lens<<=1,tim++;
	cdq(0,lens,tim);
//	cout<<F[4]<<endl;
	printf("%lld\n",F[n]*jc[n-1]%Mod);
	return 0;
}
posted @ 2021-07-15 13:44  redproblemdog  阅读(46)  评论(0编辑  收藏  举报