zoukankan      html  css  js  c++  java
  • [bzoj3456] 城市规划 [递推+多项式求逆]

    题面

    bzoj权限题面

    离线题面

    思路

    orz Miskcoo !

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

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

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

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

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

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

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

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

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

    把组合数展开

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

    整理一下:

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

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

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

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

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

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

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

    那么显然$H=F*G (mod x{n})$,即$F=H*G{-1} (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
    ",F[n]*fac[n-1]%MOD);
    }
    
  • 相关阅读:
    实验1.2 C语言上机入门 二
    如何使用OJ系统
    (第五周)工作总结
    (第五周)团队项目2
    (第五周)团队项目1
    (第五周)立项申请更新(食物链教学工具)
    (第四周)工作总结
    (第四周)四则运算单元测试
    (第四周)词频统计单元测试
    (第三周)工作总结
  • 原文地址:https://www.cnblogs.com/dedicatus545/p/9449046.html
Copyright © 2011-2022 走看看