3456: 城市规划 多项式求逆

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

题解:

好久没写过博客了……
模板题。记 f n n 的答案,那么有:
f n = 2 C n 2 i = 1 n 1 C n 1 i 1 × f i × 2 C n i 2
大概就是用总方案数减去不合法的,不合法的方案考虑1号点所在的连通块计算方案。
然后移项:
i = 1 n C n 1 i 1 × f i × 2 C n i 2 = 2 C n 2
同除 ( n 1 ) !
i = 1 n f i ( i 1 ) ! × C n i 2 ( n i ) ! = 2 C n 2 ( n 1 ) !
然后就可以看做是两个多项式相乘,用求逆求出即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=130010;
const int inf=2147483647;
const int mod=1004535809,g=3;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int Pow(int x,int y)
{
    if(y==1)return x;
    int t=Pow(x,y>>1),re=(LL)t*t%mod;
    if(y&1)re=(LL)re*x%mod;
    return re;
}
int bin[Maxn<<2];
void ntt(int *a,int n,int o)
{
    bin[0]=0;
    for(int i=1;i<n;i++)bin[i]=((bin[i>>1]>>1)|((i&1)*(n>>1)));
    for(int i=0;i<n;i++)if(i<bin[i])swap(a[i],a[bin[i]]);
    for(int i=1;i<n;i<<=1)
    {
        int tt;
        if(o==1)tt=(mod-1)/(i<<1);
        else tt=mod-1-(mod-1)/(i<<1);
        int wn=Pow(g,tt);
        for(int j=0;j<n;j+=(i<<1))
        {
            int w=1;
            for(int k=0;k<i;k++)
            {
                int t=(LL)a[i+j+k]*w%mod;w=(LL)w*wn%mod;
                a[i+j+k]=(a[j+k]-t+mod)%mod;
                a[j+k]=(a[j+k]+t)%mod;
            }
        }
    }
    if(o==-1)
    {
        int t=Pow(n,mod-2);
        for(int i=0;i<n;i++)a[i]=(LL)a[i]*t%mod;
    }
}
void work(int deg,int *a,int *b,int *tmp)
{
    if(deg==1){b[0]=Pow(a[0],mod-2);return;}
    work((deg+1)>>1,a,b,tmp);
    int m=1;while(m<(deg<<1))m<<=1;
    copy(a,a+deg,tmp);fill(tmp+deg,tmp+m,0);
    ntt(tmp,m,1),ntt(b,m,1);
    for(int i=0;i<m;i++)
    {
        b[i]=(LL)b[i]*(2-(LL)tmp[i]*b[i]%mod)%mod;
        if(b[i]<0)b[i]+=mod;
    }
    ntt(b,m,-1);fill(b+deg,b+m,0);
}
int a[Maxn<<2],b[Maxn<<2],c[Maxn<<2],d[Maxn<<2];
int n,C[Maxn],fac[Maxn],fin[Maxn],inv[Maxn];
int main()
{
    n=read();
    C[0]=C[1]=C[2]=1;for(int i=3;i<=n;i++)C[i]=(C[i-1]+i-1)%(mod-1);
    fac[0]=1;for(int i=1;i<=n;i++)fac[i]=(LL)fac[i-1]*i%mod;
    inv[0]=inv[1]=1;for(int i=2;i<=n;i++)inv[i]=(LL)(mod-mod/i)*inv[mod%i]%mod;
    fin[0]=1;for(int i=1;i<=n;i++)fin[i]=(LL)fin[i-1]*inv[i]%mod;
    a[0]=a[1]=d[1]=1;d[0]=0;
    for(int i=2;i<=n;i++)a[i]=(LL)Pow(2,C[i])*fin[i]%mod;
    work(n+1,a,b,c);
    for(int i=2;i<=n;i++)d[i]=(LL)Pow(2,C[i])*fin[i-1]%mod;
    int m=1;while(m<=(n<<1))m<<=1;
    ntt(b,m,1),ntt(d,m,1);
    for(int i=0;i<=m;i++)b[i]=(LL)b[i]*d[i]%mod;
    ntt(b,m,-1);
    printf("%d",(LL)b[n]*fac[n-1]%mod);
}


猜你喜欢

转载自blog.csdn.net/baidu_36797646/article/details/81045384