zoukankan      html  css  js  c++  java
  • UOJ#428. 【集训队作业2018】普通的计数题

    #428. 【集训队作业2018】普通的计数题

    模型转化好题

    所以变成统计有标号合法的树的个数。

    合法限制:

    1.根标号比子树都大

    2.如果儿子全是叶子,数量B中有

    3.如果存在一个儿子不是叶子,数量A中有

    然后考虑DP

    直接枚举根的儿子的情况

    cdq分治NTT还是很恶心的

    不光是自己卷自己,还是互相卷

    进行一番化简和平移之后,可以转化为cdq分治NTT的形式:

    怎么好做怎么来。

    反正我最后推的式子有如下特点(式子就不写了):

    为了方便,钦定g[0],f[0],g[1],f[1]都是0

    对于f,a是固定的,a向右平移一下,然后就是cdq分治的模板题了

    对于g,当cdq的分治区间l不是0的时候,要F作为[l,mid],G作为[ql,qr],和G作为[l,mid],F作为[ql,qr]做两遍

    这样其实剩下g[n]=g[0]*f[n],但是g[0]=0,所以不用管

    代码:

    const int N=240000+5;
    int jie[N],inv[N];
    int f[N],g[N];
    int n,sa,sb;
    int ta[N],b[N],a[N];
    void divi(int l,int r,int ql,int qr){
        // cout<<" divi "<<l<<" "<<r<<" ql "<<ql<<" qr "<<qr<<endl;
        if(l==0&&r==1){
            f[0]=f[1]=g[0]=g[1]=0;
            return;
        }
        if(l==r){
            f[l]=ad(mul(f[l],jie[l-1]),b[l-1]);
            g[l]=ad(f[l],mul(g[l],jie[l-1]));
            f[l]=mul(f[l],inv[l-1]);
            g[l]=mul(g[l],inv[l]);
            return;
        }
        int mid=(l+r)>>1;
        int qmd=(ql+qr)>>1;
        divi(l,mid,ql,qmd);
        Poly A,G;
        A.resize(qr-ql+1);
        G.resize(mid-l+1);
        for(reg i=ql;i<=qr;++i){
            A[i-ql]=a[i];
        }
        for(reg i=l;i<=mid;++i){
            G[i-l]=g[i];
        }
        A*=G;
        for(reg i=mid+1;i<=r;++i){
            f[i]=ad(f[i],A[i-l]);
        } 
        
        if(l==0){
            Poly F;G.clear();
            F.resize(mid-l+1);
            G.resize(mid-l+1);
            for(reg i=l;i<=mid;++i){
                F[i-l]=f[i];
                G[i-l]=g[i];
            }
            F=F*G;
            for(reg i=mid+1;i<=r;++i){
                g[i]=ad(g[i],F[i]);
            }
        }else{
            Poly F;G.clear();
            F.resize(qr-ql+1);
            G.resize(mid-l+1);
            for(reg i=l;i<=mid;++i){
                G[i-l]=g[i];
            }
            for(reg i=ql;i<=qr;++i){
                F[i-ql]=f[i];
            }
            F=F*G;
            for(reg i=mid+1;i<=r;++i){
                g[i]=ad(g[i],F[i-l]);
            }
            F.clear();G.clear();
            F.resize(mid-l+1);
            G.resize(qr-ql+1);
            for(reg i=ql;i<=qr;++i){
                G[i-ql]=g[i];
            }
            for(reg i=l;i<=mid;++i){
                F[i-l]=f[i];
            }
            F=F*G;
            for(reg i=mid+1;i<=r;++i){
                g[i]=ad(g[i],F[i-l]);
            }
        }
        divi(mid+1,r,ql,qmd);
    }
    int main(){
        rd(n);rd(sa);rd(sb);int x;
        for(reg i=1;i<=sa;++i){rd(x);ta[x]=1;}
        for(reg i=1;i<=sb;++i){rd(x);b[x]=1;}
        if(n==1){
            puts("1");return 0;
        }
        int m;
        for(m=1;m<=n;m<<=1);
        jie[0]=1;
        for(reg i=1;i<=m;++i) jie[i]=mul(jie[i-1],i);
        inv[m]=qm(jie[m],mod-2);
        for(reg i=m-1;i>=0;--i) inv[i]=mul(inv[i+1],i+1);
    
        for(reg i=1;i<=m;++i){
            a[i]=mul(ta[i-1],inv[i-1]);
        }
        a[0]=0;
    
        divi(0,m-1,0,m-1);
        ll ans=f[n];
        ans=mul(ans,jie[n-1]);
        ot(ans);
        return 0;
    }
    

    树形结构很巧妙啊

    f,g互相卷的分治NTT第一次写,还是举一个0,1,2,3,4,5,6,7的例子最好理解了!

  • 相关阅读:
    HZOJ 通讯
    HZOJ 礼物
    HZOI 可怜与超市
    高二小假期集训—D5
    [BZOJ3566][SHOI2014]概率充电器
    [***]HZOI20190714 T2熟练剖分
    20190714(又一次翻车……)
    HZOI20190714 T3建造游乐场
    模板—慢速乘
    模板—十进制快速幂
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10994486.html
Copyright © 2011-2022 走看看