[bzoj3456] 城市规划 [递推+多项式求逆]

题面

bzoj权限题面

离线题面

思路

orz Miskcoo !

先考虑怎么算这个图的数量

设$f(i)$表示$i$个点的联通有标号无向图个数,$g(i)$表示$n$个点的有标号无向图个数(可以不连通)

那么,显然因为$n$个点有$\binom{n}{2}$条边可以选放不放,所以$g(n)=2^{\binom{n}{2}}$

同时,我们考虑枚举标号为$1$的点所在的联通块大小,以此用$f$来表示$g$

那么,显然可以得到以下式子:

$g(n)=\sum_{i=1}^{n-1}\binom{n-1}{i-1}f(i)g(n-i)$,就是表示从除了1以外的$n-1$个点里面选出$i-1$个点和$1$一起构成一个联通无向图,即$f(i)

然后剩下的$n-i$个点再随便选,即$g(n-i)$

那么,我们把$g$的公式代入,得到这个东西:

$2{\binom{n}{2}}=\sum_{i=1}\binom{n-1}{i-1}f(i)2^{\binom{n-i}{2}}$

把组合数展开

$2{\binom{n}{2}}=\sum_{i=1}f(i)2^{\binom{n-i}{2}}\frac{(n-1)!}{(i-1)!(n-i)!}$

整理一下:

$\frac{2{\binom{n}{2}}}{(n-1)!}=\sum_{i=1}\frac{f(i)}{(i-1)!} \frac{2^{ \binom{n-i}{2}} } {(n-i)!}$

发现这个式子是一个卷积的形式!

我们设三个多项式$F,H,G$,并令:

$F(n)=\frac{f(n)}{(n-1)!}$

$G(n)=\frac{ 2^{ \binom{n}{2}} }{n!}$

$H(n)=\frac{ 2^{ \binom{n}{2}} }{(n-1)!}$

此处$F(n)$表示$F$的$n$次项系数

那么显然$H=F*G (mod x{n})$,即$F=H*G (mod x^{n})$,而且这三个东西都可以很容易预处理出来

所以我们多项式求逆,搞出来$G$的逆元,再和$H$乘在一起,取$n$次项乘上$(n-1)!$,就是答案$f(n)$了

Code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
#include<cmath>
#define ll long long
#define MOD 1004535809
using namespace std;
inline int read(){
	int re=0,flag=1;char ch=getchar();
	while(ch>'9'||ch<'0'){
		if(ch=='-') flag=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
	return re*flag;
}
ll F[600010],G[600010],GINV[600010],H[600010],tmp[600010],x[600010],y[600010],r[600010],g=3,ginv;
int lim,cnt,n;
ll qpow(ll a,ll b){
	ll re=1;
	while(b){
		if(b&1) re=re*a%MOD;
		a=a*a%MOD;b>>=1; 
	}
	return re;
}
ll add(ll a,ll b){
	a+=b;
	return (a>=MOD)?a-MOD:a;
}
ll dec(ll a,ll b){
	a-=b;
	return (a<0)?a+MOD:a;
}
void ntt(ll *a,ll type){
	int i,j,k;ll x,y,w,wn,inv,mid;
	for(i=0;i<lim;i++) if(i<r[i]) swap(a[i],a[r[i]]);
	for(mid=1;mid<lim;mid<<=1){
		wn=qpow(((type==1)?g:ginv),(MOD-1)/(mid<<1));
		for(j=0;j<lim;j+=(mid<<1)){
			w=1;
			for(k=0;k<mid;k++,w=w*wn%MOD){
				x=a[j+k];y=a[j+k+mid]*w%MOD;
				a[j+k]=add(x,y);
				a[j+k+mid]=dec(x,y);
			}
		}
	}
	if(~type) return;
	inv=qpow(lim,MOD-2);
	for(i=0;i<lim;i++) a[i]=a[i]*inv%MOD;
}
void Solve(ll *a,ll *b,int len){
	if(len==1){b[0]=qpow(a[0],MOD-2);return;}
	int i,mid=(len+1)>>1;
	Solve(a,b,mid);
	lim=1;cnt=0;memset(r,0,sizeof(r));
	while(lim<=(n+mid*2)) lim<<=1,cnt++;
	for(i=0;i<lim;i++) r[i]=((r[i>>1]>>1)|((i&1)<<(cnt-1))),x[i]=a[i],y[i]=b[i];
	ntt(x,1);ntt(y,1);
	for(i=0;i<lim;i++) x[i]=y[i]*y[i]%MOD*x[i]%MOD;
	ntt(x,-1);
	for(i=0;i<len;i++) b[i]=dec((b[i]<<1)%MOD,x[i]);
}
ll fac[130010],finv[130010];
void initf(){
	int i;fac[0]=fac[1]=finv[0]=finv[1]=1;
	for(i=2;i<=130000;i++) fac[i]=fac[i-1]*i%MOD;
	finv[130000]=qpow(fac[130000],MOD-2);
	for(i=130000;i>2;i--) finv[i-1]=finv[i]*i%MOD;
}
int main(){
	n=read();ll i,tmp;
	initf();ginv=qpow(g,MOD-2);
	G[0]=1;
	for(i=1;i<=n;i++){
		tmp=qpow(2,(i*(i-1)>>1)%(MOD-1));
		G[i]=tmp*finv[i]%MOD;H[i]=tmp*finv[i-1]%MOD;
	}
	Solve(G,GINV,n+1);
	lim=1;cnt=0;
	while(lim<=((n+1)<<1)) lim<<=1,cnt++;
	for(i=0;i<lim;i++) r[i]=((r[i>>1]>>1)|((i&1)<<(cnt-1)));
	ntt(GINV,1);ntt(H,1);
	for(i=0;i<lim;i++) F[i]=GINV[i]*H[i]%MOD;
	ntt(F,-1);
	printf("%lld\n",F[n]*fac[n-1]%MOD);
}
posted @ 2018-08-09 15:08  dedicatus545  阅读(128)  评论(0编辑  收藏  举报