• LOJ#6538. 烷基计数 加强版 加强版/LG P6598 烷烃计数


    Description

    $n$ 个碳原子的烷烃共有多少种同分异构体?

    提示:如果你不知道什么是烷烃,那么你可以认为这个问题等价于求 $n$ 个点的无标号无根树并满足每个点的度数 $leq 4$ 的树的个数。

    Solution

    先对烷基计数,即求$n$个碳原子的烷基共有多少种

    先忽视烷基中单独的那个电子,那么烷基可以视为三叉树,如果有不饱和的C,可以看做其有一个大小为0的子树

    根据burnside引理,设$n$个C的烷基有$f_n$个,那么:

    $$f_n=frac{sum _{i+j+k=n-1}f_if_jf_k+3sum _{2i+j=n-1}f_if_j+2sum _{3i=n-1}f_i}{6} $$

    设$A(x)$是其生成函数,那么:

    $$A(x)=frac{A^3(x)+3A(x^2)A(x)+2A(x^3)}{6}x+1 $$

    递推$A$可以用分治FFT或者牛顿迭代解决

    再尝试证明结论:

    在一个已知的烷烃(一个无根树)中,设$p$为将一个烷烃中的C特殊标记后,有多少不同的烷烃,$q$为将一个烷烃中的碳碳键特殊标记后,有多少不同的烷烃,$b$为该烷烃是否有两个重心且两重心两侧同构的布尔变量,那么:

    $$p-q+b=1$$

    分类讨论,当$b=false$时,任选一个重心,该重心被标记后必不会与其它点标记的情况同构,如果有两个C被标记后同构,那么这两个C向父亲方向(以重心为根)的碳碳键被标记后一定同构

    当$b=true$时,除重心外的C同上,两个重心只产生一个同构,两重心之间连边产生一个同构,所以$p=q$

    那么枚举所有的无根树,就有$sum p-sum q+sum b=sum 1$,其实题中就是求$sum 1$

    所以现在要计算$sum p$,$sum q$和$sum b$

    计算$sum p$:

      就是计算有根树的个数(将被标记的C看作根),用与计算烷基相似的方法计算有根树的个数,最终得到OGF$P(x)$为:

    ·  $$P(x)=frac{A^4(x)+6A(x^2)A^2(x)+3A^2(x^2)+8A(x^3)A(x)+6A(x^4)}{24}x+1 $$

    计算$sum q$:

      可以看做让两个烷基之间成键,最终得到OGF$Q(x)$为:

      $$Q(x)=frac{(A^2(x)-1)^2+A(x^2)-1}{2}+1 =frac{A^2(x)+A(x^2)-2A^2(x)}{2}+1 $$

      在$A(x)$平方时将每个情况计算了两次,但是两个烷基相同的情况只计算了一次,所以写成这样

    计算$sum b$

      发现就是将两个相同的烷基之间成键,OGF为$A(x^2)$

    答案OGF为$P(x)-Q(x)+A(x^2)$

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int s,tot,rev[800005],n,T;
    const int mod=998244353;
    long long A[800005],inv[800005],A2[800005],A3[800005],ans[800005],C[800005];
    inline long long read(){
        long long f=1,w=0;
        char ch=0;
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')w=(w<<1)+(w<<3)+ch-'0',ch=getchar();
        return f*w;
    }
    long long ksm(long long a,long long p){
        long long ret=1ll;
        while(p){
            if(p&1)(ret*=a)%=mod;
            (a*=a)%=mod,p>>=1;
        }
        return ret;
    }
    void ntt(long long *a,int N,int in){
        for(int i=0;i<N;i++)if(i<rev[i])swap(a[i],a[rev[i]]);
        for(int i=1;i<N;i<<=1){
            long long wn=ksm(3,(mod-1)/i/2);
            if(in==-1)wn=ksm(wn,mod-2);
            for(int j=0;j<N;j+=i<<1){
                long long w=1;
                for(int k=j;k<i+j;k++){
                    long long x=a[k],y=w*a[k+i]%mod;
                    a[k]=(x+y)%mod,a[k+i]=(mod+x-y)%mod,(w*=wn)%=mod;
                }
            }
        }
        if(in==-1)for(int i=0;i<N;i++)(a[i]*=inv[N])%=mod;
    }
    void solve(int l,int r){
        if(l==r){(A[l]+=(l%3==1)*A[l/3]*inv[3]%mod)%=mod;return;}
        int mid=(l+r)>>1;
        solve(l,mid),s=2,tot=1;
        static long long a1[800005],a2[800005],b[800005],c[800005];
        while(s<=(2*(r-l+1)))s<<=1,++tot;
        for(int i=0;i<s;i++)a1[i]=i<=mid-l?A[l+i]:0,a2[i]=i<=r-l?A[i]:0,b[i]=(i<=r-l&&!(i&1))?A[i/2]:0;
        for(int i=0;i<s;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(tot-1));
        ntt(a1,s,1),ntt(a2,s,1),ntt(b,s,1);
        for(int i=0;i<s;i++)c[i]=(a1[i]*a2[i]%mod*a2[i]%mod*(l?inv[2]:inv[6])%mod+a1[i]*b[i]%mod*inv[2]%mod)%mod;
        ntt(c,s,-1);
        for(int i=mid-l;i<=r-l-1;i++)(A[l+i+1]+=c[i])%=mod;
        solve(mid+1,r);
    }
    int main(){
        inv[0]=inv[1]=1,T=read();
        for(int i=2;i<=800000;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
        A[0]=1,solve(0,100000),s=2,tot=1;
        while(s<=300000)s<<=1,++tot;
        for(int i=0;i<=100000;i++)A2[i]=i&1?0:A[i/2],A3[i]=i%3?0:A[i/3];
        for(int i=0;i<=100000;i++)if(i%4==1)ans[i]=A[i/4]*inv[4]%mod;
        for(int i=0;i<=100000;i++)if(!(i&1))ans[i]=A[i/2];
        for(int i=0;i<s;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(tot-1));
        ntt(A,s,1),ntt(A2,s,1),ntt(A3,s,1);
        for(int i=0;i<s;i++)C[i]=(A2[i]*A2[i]%mod*inv[8]%mod+A[i]*A[i]%mod*A2[i]%mod*inv[4]%mod+A[i]*A3[i]%mod*inv[3]%mod+A[i]*A[i]%mod*A[i]%mod*A[i]%mod*inv[24]%mod)%mod;
        ntt(C,s,-1);
        for(int i=0;i<=100000;i++)(ans[i+1]+=C[i])%=mod;
        for(int i=0;i<s;i++)C[i]=(A[i]*A[i]%mod*inv[2]%mod+A2[i]*inv[2]%mod-A[i]+mod)%mod;
        ntt(C,s,-1);
        for(int i=0;i<=100000;i++)(ans[i]+=mod-C[i])%=mod;
        for(;T;T--)n=read(),printf("%lld
    ",ans[n]);
        return 0;
    }
    烷烃计数
  • 相关阅读:
    scala -- 柯里化
    scala-- 高阶函数
    neo4j 数据库导入导出
    scala -- 模式匹配
    命名空间
    [BZOJ1018][SHOI2008]堵塞的交通traffic 线段树维护连通性
    [BZOJ1017][JSOI2008]魔兽地图DotR 树形dp
    [BZOJ1260][CQOI2007]涂色paint 区间dp
    [bzoj1787][Ahoi2008]Meet 紧急集合
    [BZOJ2243][SDOI2011]染色
  • 原文地址:https://www.cnblogs.com/JDFZ-ZZ/p/14477173.html
Copyright © 2020-2023  润新知