• CF1580B Mathematics Curriculum


    Solution

    一个值 (x) 的最大值个数就是它所在位置往前做单调栈,往后做单调栈后,两个单调栈的大小的和。这个东西对应于笛卡尔树中,(x) 所在位置的深度(考虑建树过程)。所以问题就转换为求第 (m) 层有 (k) 个节点的笛卡尔树有多少个。

    可以考虑增量更新。每次加入一个新的 (i),它是当前最大的,那么它就是树根。所以可以考虑树形dp。状态和树的大小,当前深度,(m) 层已经有的点个个数。所以记 (dp_{i,j,k}) 表示当前子树大小为 (i),根在第 (j) 层,第 (m) 层有 (k) 个点的树的个数。由于只有两个儿子,所以直接枚举一边的状态,有

    [dp_{i,j,k}=sum_{a,b} inom{i-1}{a} dp_{a,j+1,b} imes dp_{i-1-a,j+1,k-b-[j=m]} ]

    这样就有一个 (O(n^5)) 的算法,卡卡常就能过。具体的,对于 (j>m)(k=0) 的状态,已经和树高没有关系了,方案直接就是 (i!)。还有其他剪枝,不再赘述。

    #include<stdio.h>
    #include<algorithm>
    #include<string.h>
    using namespace std;
    
    inline int read(){
        int x=0,flag=1; char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')flag=0;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
        return flag? x:-x;
    }
    
    typedef long long ll;
    
    const int N=1e2+3;
    
    int n,m,K,Mod;
    ll C[N][N],dp[N][N][N],fac[N];
    
    ll dfs(int i,int j,int k){
        if(i<k||i+1<k*2||k<0) return 0;
        if(j>m) return k? 0:fac[i];
        if(!i) return !k;
        ll &ret=dp[i][j][k];
        if(~ret) return ret; else ret=0;
        for(int a=0;a<=i-1;a++)
            for(int b=0;b<=k;b++)
                ret=(ret+C[i-1][a]*dfs(a,j+1,b)%Mod*dfs(i-1-a,j+1,k-b-(j==m))%Mod)%Mod;
        return ret;
    }
    
    int main(){
        n=read(),m=read(),
        K=read(),Mod=read();
        memset(dp,-1,sizeof(dp));
        fac[0]=1; for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%Mod;
        for(int i=0;i<=n;i++) C[i][0]=1;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%Mod;
        printf("%lld
    ",dfs(n,1,K));
    }
    
  • 相关阅读:
    基本命令
    Unicode Locale
    二进制查看编辑
    java reg
    java util
    js util
    跑到X
    [转]Linux AIO :libaio
    [转]c++ atomic操作
    [转] dpdk笔记
  • 原文地址:https://www.cnblogs.com/wwlwQWQ/p/15380531.html
Copyright © 2020-2023  润新知