ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2019-06-09 19:44:00  阅读:322  来源: 互联网

标签:qr int 分治 mid 428 2018 UOJ ql cdq


#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的例子最好理解了!

 

标签:qr,int,分治,mid,428,2018,UOJ,ql,cdq
来源: https://www.cnblogs.com/Miracevin/p/10994486.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有