• 【BZOJ3456】城市规划-多项式求逆


    测试地址:城市规划
    题目大意:n个点带标号简单无向连通图(即无重边,无自环)的数目。
    做法:本题需要用到多项式求逆。
    如果不要求连通,这题就是水题了,答案显然为2Cn2(即枚举每条边选或不选)。
    然而这题显然没那么简单,我们令f(n)为我们要求的答案,g(n)=2Cn2,有如下递推式:
    g(n)=i=1n1Cn1i1f(i)g(ni)
    上面式子的意义实质上是枚举点1所在的连通块的大小i进行转移。
    当然,我们要求的不是g而是f,所以我们需要找到一个方式用g表示出f
    把上面的组合数拆开,得到:
    g(n)=(n1)!i=1n1f(i)(i1)!g(ni)(ni)!
    两边同除一个(n1)!,这就是一个卷积形式的式子,于是我们可以用三个生成函数(多项式)来表示这三个部分:
    A(x)=B(x)C(x)
    C为包含f(i)的那个部分,于是C(x)=A(x)B1(x),其中C1(x)是多项式C的逆。
    于是接下来就涉及多项式求逆的问题了,在这里,令C的最高次项的次数为n,那么它的逆C1(x)应该满足:
    C(x)C1(x)1(modxn+1)
    于是问题转化成求一个模意义下的多项式D(x),使得:
    C(x)D(x)1(modxn)
    假设我们已经求出一个D(x)满足:
    C(x)D(x)1(modxn2)
    于是我们有:
    D(x)D(x)0(modxn2)
    两边平方,把模数扩展到n(也有可能是n+1,反正不小于n,不管):
    D2(x)2D(x)D(x)D2(x)(modxn)
    两边再乘一个C(x),有:
    D(x)2D(x)C(x)D2(x)(modxn)
    而我们知道,C(x)在模x意义下的逆元,就等于它常数项的逆元。于是这样我们就能倍增+NTT算出多项式的逆了,算法的时间复杂度为:
    T(n)=T(n2)+O(nlogn)=O(nlogn)
    这样我们就解决了这一题。注意因为上面的所有计算都是在模意义下进行的,所以每一步都要将多项式取模(其实就是截取前面的某一段)。
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll mod=1004535809;
    const ll g=3;
    ll n,rev[600010]={0},fac[600010],inv[600010];
    ll A[600010]={0},B[600010]={0},C[600010]={0};
    ll p[600010]={0},s[600010]={0};
    
    ll power(ll a,ll b)
    {
        ll s=1,ss=a;
        b=(b%(mod-1)+mod-1)%(mod-1);
        while(b)
        {
            if (b&1) s=s*ss%mod;
            ss=ss*ss%mod;b>>=1;
        }
        return s;
    }
    
    ll NTT(ll *a,ll type,ll n)
    {
        for(ll i=0;i<n;i++)
            if (i<rev[i]) swap(a[i],a[rev[i]]);
        for(ll mid=1;mid<n;mid<<=1)
        {
            ll W=power(g,type*(mod-1)/(mid<<1));
            for(ll l=0;l<n;l+=(mid<<1))
            {
                ll w=1;
                for(ll k=0;k<mid;k++,w=w*W%mod)
                {
                    ll x=a[l+k],y=w*a[l+mid+k]%mod;
                    a[l+k]=(x+y)%mod;
                    a[l+mid+k]=(x-y+mod)%mod;
                }
            }
        }
        if (type==-1)
        {
            ll inv=power(n,mod-2);
            for(ll i=0;i<=n;i++)
                a[i]=a[i]*inv%mod;
        }
    }
    
    ll calc_rev(ll limit)
    {
        ll bit=0,x=1;
        while(x<=limit) bit++,x<<=1;
        rev[0]=0;
        for(ll i=1;i<=x;i++)
            rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
        return x;
    }
    
    void calc_inv(ll *a,ll len)
    {
        if (len==1)
        {
            s[0]=power(a[0],mod-2);
            return;
        }
        calc_inv(a,(len+1)>>1);
    
        ll x=calc_rev((len<<1)-1);
        memset(p,0,sizeof(p));
        for(ll i=0;i<len;i++)
            p[i]=a[i];
        NTT(s,1,x),NTT(p,1,x);
        for(ll i=0;i<=x;i++)
            s[i]=(2ll*s[i]-p[i]*s[i]%mod*s[i]%mod+mod)%mod;
        NTT(s,-1,x);
        for(ll i=len;i<=x;i++)
            s[i]=0;
    }
    
    int main()
    {
        scanf("%lld",&n);
        fac[0]=fac[1]=1;
        for(ll i=1;i<=n;i++)
            fac[i]=fac[i-1]*i%mod;
        inv[n]=power(fac[n],mod-2);
        for(ll i=n;i>=1;i--)
            inv[i-1]=inv[i]*i%mod;
    
        for(ll i=0;i<=n;i++)
        {
            if (i>0) A[i]=power(2ll,i*(i-1)/2ll)*inv[i-1]%mod;
            B[i]=power(2ll,i*(i-1)/2ll)*inv[i]%mod;
        }
        calc_inv(B,n+1);
    
        ll x=calc_rev(n);
        NTT(s,1,x),NTT(A,1,x);
        for(ll i=0;i<=x;i++)
            C[i]=s[i]*A[i]%mod;
        NTT(C,-1,x);
        printf("%lld",C[n]*fac[n-1]%mod);
    
        return 0;
    }
  • 相关阅读:
    leetcode Power of Two
    leetcode Merge Two Sorted Lists
    Mac linux 安装memcached服务 用法
    Vsftp配置都没有问题 连接不上 530 Login incorrect 解决方法
    mysql 远程连接不上 %用户已经添加了
    Centos yum 安装mysql报错 No package mysql-server available.
    Linux 查询程序安装路径 是否安装
    php报错 Call to undefined function mb_stripos()
    mac编译openssl扩展报错 openssl.c:44:10: fatal error: 'openssl/evp.h' file not found
    在安装mysqli的时候,出现error: ext/mysqlnd/mysql_float_to_double.h: No such file or direc
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793319.html
Copyright © 2020-2023  润新知