【集训队互测2013】城市规划

Description

求出 n 个点的简单(无重边无自环)无向连通图数目。
方案数mod 1004535809 ( 479 * 221 + 1 )。

Data Constraint

n<= 1.3105

Solution

fn 表示当点数为 n 时的答案,考虑容斥。
考虑不合法情况,枚举一号点所在的连通图的大小k,考虑组成此连通图的选点方案,为 Ck1n1 ,此连通块外的边可选可不选,选边方案数为 2C2nk ,得到 Fn 的转移方程

fn=2C2nk=1n1fkCk1n12C2nk

但这样显然不可做,我们尝试移项,可得

2C2n=k=1nfkCk1n12C2nk

把中间的组合数拆开
2C2n=k=1nfk(n1)!(k1)!(nk)!2C2nk

再移一下项,把相关的项移到一起,可得
2C2n(n1)!=k=1nfk(k1)!2C2nk(nk)!

Gn = 2C2n(n1)! Fk = fk(k1)! Hk = 2C2nk(nk)! ,便可得
Gn=k=1nFkHnk

则有
G(x)=F(x)H(x)

F(x)=G(x)H1(x)

多项式求逆?!求出 F(x) 便可顺势求出 fn 了。
不会多项式求逆的猛戳 这里

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

#define fo(i,j,l) for(int i=j;i<=l;++i)
#define fd(i,j,l) for(int i=j;i>=l;--i)

using namespace std;
typedef long long ll;
const ll N=14e4,M=8*N,mo=1004535809;

ll t[M],w[M],n,ws,len,jc[N],h[M],g[M],r[M],b[M],tt[M];

ll ksm(ll o,ll t)
{
    ll y=1; t=t%(mo-1);
    for(;t;t>>=1,o=o*o%mo)
    if(t&1)y=y*o%mo;
    return y;
}

void NNT(ll *a,int n,int ws,int sig)
{
    w[0]=1; w[1]=ksm(3,(mo-1)/n);
    fo(i,2,n)w[i]=w[i-1]*w[1]%mo;
    fo(i,0,n-1)
    {
        int p=0;
        for(int tp=i,C=0;C<ws;++C,tp>>=1)p=(p<<1)+(tp&1);
        t[p]=a[i];
    }
    ll v,u;
    for(int m=2;m<=n;m<<=1)
    {
        int half=m/2,bei=n/m;
        fo(i,0,half-1)
        {
            v=(sig==1?w[i*bei]:w[n-i*bei]);
            for(int j=i;j<n;j+=m)
            u=t[j+half]*v%mo,t[j+half]=(t[j]-u+mo)%mo,t[j]=(t[j]+u)%mo;
        }
    }
    fo(i,0,n-1)a[i]=t[i];
    if(sig==-1){
        ll ny=ksm(n,mo-2);
        fo(i,0,n-1)a[i]=a[i]*ny%mo;
    }
}

ll C(ll n)
{return n*(n-1)/2;}

void oppsite(ll *a,int cd)
{
    tt[0]=ksm(a[0],mo-2); 
    fo(i,1,4*cd-1)tt[i]=0;
    for(int y=1,u=0;y<cd;y<<=1,++u)
    {
        int len=y*4;
        fo(i,0,len-1)r[i]=tt[i];
        NNT(r,len,u+2,1);
        fo(i,0,len-1)r[i]=r[i]*r[i]%mo;
        fo(i,0,len/2-1)b[i]=a[i];
        fo(i,len/2,len-1)b[i]=0;
        NNT(b,len,u+2,1);
        fo(i,0,len-1)b[i]=b[i]*r[i]%mo;
        NNT(b,len,u+2,-1);
        fo(i,0,2*y-1)tt[i]=(2*tt[i]-b[i]+mo)%mo;
    }
    fo(i,0,cd-1)a[i]=tt[i];
}

int main()
{
    cin>>len;
    int ws=0,y=1;
    while(y<len*2)y<<=1,++ws;
    jc[0]=1;  fo(i,1,len)jc[i]=jc[i-1]*ksm(i,mo-2)%mo;
    fo(i,1,len)h[i]=ksm(2,C(i))*jc[i-1]%mo;
    NNT(h,y,ws,1); 
    fo(i,0,len)g[i]=ksm(2,C(i))*jc[i]%mo;
    oppsite(g,y);
    NNT(g,y,ws,1);
    fo(i,0,y-1)g[i]=g[i]*h[i]%mo;
    NNT(g,y,ws,-1);
    ll ans=g[len];
    fo(i,2,len-1)ans=ans*i%mo;
    printf("%lld",ans);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值