0
点赞
收藏
分享

微信扫一扫

bzoj 3456 城市规划


3456: 城市规划

Time Limit: 40 Sec Memory Limit: 256 MB
Submit: 732 Solved: 413
[Submit][Status][Discuss]
Description

刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了.
刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通. 为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案.
好了, 这就是困扰阿狸的问题. 换句话说, 你需要求出n个点的简单(无重边无自环)无向连通图数目.
由于这个数字可能非常大, 你只需要输出方案数mod 1004535809(479 * 2 ^ 21 + 1)即可.

Input

仅一行一个整数n(<=130000)

Output

仅一行一个整数, 为方案数 mod 1004535809.

Sample Input

3

Sample Output

4

HINT

对于 100%的数据, n <= 130000

Source

【分析】

一道经典(恶心)的图的计数问题…

题意:求n个点无向连通图个数

令f(i)为i个点无向连通图的个数,。

则f(i)=2C(i,2)−∑i−1j=1f(j)∗C(i−1,j−1)∗2C(i−j,2)

证明:考虑用i个点所有无向图的个数减去不连通的无向图的个数。枚举1号点所在的连通块的大小j,方案为f(j),那么我们要从2~i的点中取j−1个点和1号点形成一个连通块,方案为C(i−1,j−1),剩下的i−j个点之间任意连边。方案数位2C(i−j,2)

两边同时除以fac(i−1),并展开C(i−1,j−1),然后移项。

得到 2C(i,2)fac(i−1)=∑ij=1f(j)fac(j−1)∗2C(i−j,2)fac(i−j)

发现这他喵的是个卷积…我已经写不动公式了…太TM麻烦了…

然后构造三个多项式

A[i]=f(i)fac(i−1)

B[i]=2C(i,2)fac(i)

C[i]=2C(i,2)fac(i−1)

A∗B=C

A=C∗B−1

得到A中xn项的系数,再乘以fac(n−1)即为答案。
用到多项式求逆,不会的同学自行百度

【代码】

//bzoj 3456 城市规划 
#include<bits/stdc++.h>
#define M 130000
#define ll long long
#define fo(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
const int mxn=600005;
const int mod=1004535809;
int n,m,L,N,ans;
int fac[mxn],inv[mxn];
int a[mxn],b[mxn],c[mxn],R[mxn];
inline void init()
{
    fac[0]=inv[0]=inv[1]=1;
    fo(i,1,M) fac[i]=(ll)fac[i-1]*i%mod;
    fo(i,2,M) inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
    fo(i,1,M) inv[i]=(ll)inv[i]*inv[i-1]%mod;
}
inline int power(int x,int k)
{
    int res=1;
    while(k)
    {
        if(k&1) res=(ll)res*x%mod;
        x=(ll)x*x%mod,k>>=1;
    }
    return res;
}
inline void NTT(int *a,int f)
{
    fo(i,0,n-1) if(i<R[i]) swap(a[i],a[R[i]]);
    for(int i=1;i<n;i<<=1)
    {
        int wn=power(3,(mod-1)/(i<<1));
        for(int j=0;j<n;j+=(i<<1))
        {
            int w=1;
            for(int k=0;k<i;k++,w=(ll)w*wn%mod)
            {
                int x=a[j+k],y=(ll)w*a[j+k+i]%mod;
                a[j+k]=(x+y)%mod;
                a[j+k+i]=(x-y+mod)%mod;
            }
        }
    }
    if(f==-1)
    {
        reverse(a+1,a+n);
        int rev=power(n,mod-2);
        fo(i,0,n-1) a[i]=(ll)a[i]*rev%mod;
    }
}
inline void getinv(int Nt)
{
    if(Nt==1) {b[0]=1;return;}
    getinv(Nt+1>>1);
    n=Nt;int i,m=n+n;
    for(n=1,L=0;n<=m;n<<=1) L++;
//  printf("%d %d\n",Nt,n);
    fo(i,0,n-1) R[i]=(R[i>>1]>>1)|((i&1)<<L-1);
    fo(i,0,Nt-1) c[i]=a[i];fo(i,Nt,n) c[i]=0;
    NTT(b,1),NTT(c,1);
    fo(i,0,n)
    {
        b[i]=(ll)(2-(ll)b[i]*c[i]%mod)*b[i]%mod;
        if(b[i]<0) b[i]+=mod;
    }
    NTT(b,-1);
    fo(i,Nt,n) b[i]=0;
}
int main()
{
    init();
    scanf("%d",&N);
    fo(i,0,N)
      a[i]=(ll)power(2,(ll)i*(i-1)/2%(mod-1))*inv[i]%mod;
    m=N;for(n=1;n<=m;n<<=1) L++;
    getinv(n);
    memset(c,0,sizeof c);
    memset(a,0,sizeof a);
    m=N+N;for(n=1,L=0;n<=m;n<<=1) L++;
    fo(i,0,n-1) R[i]=(R[i>>1]>>1)|((i&1)<<L-1);
    fo(i,1,N)
      c[i]=(ll)power(2,(ll)i*(i-1)/2%(mod-1))*inv[i-1]%mod;
    NTT(b,1),NTT(c,1);
    fo(i,0,n) a[i]=(ll)b[i]*c[i]%mod;
    NTT(a,-1);

    printf("%lld\n",(ll)a[N]*fac[N-1]%mod);
    return 0;
}


举报

相关推荐

0 条评论