[bzoj3456] 城市规划 [递推+多项式求逆]
题面
思路
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);
}