[AGC024] E - Sequence Growing Hard 树型DP

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/DOFYPXY/article/details/80387929

把问题看成每次向序列中添加一个[1,K]的数,假设我们要填的数是x,因为要保证字典序变大,那么所填的位置往后的第一个非x数必须<x。但考虑向一串x中的任意位置再填一个x都是等价的,所以等同于只能在一个<x的数之前或者序列末尾填这个x
那我们把填一个数看成一个二元组(t,w),表示在第t次填了数w,假设把这个w填的位置后面的那个<w的数看成它的父亲,那么就有了一个树型结构。(可以把序列末尾看成一个(t=0,w=0)
那么问题就转化成了计满足一下条件的树的个数:
1. troot=0,wroot=0
2. tx互不相同,wx[0,K]
3. tx>tfax
4. wx>wfax
于是我们可以设fi,j表示有i个点,wroot=j的树的个数。考虑转移,我们可以给根节点添加一个大小为kwroot>j的子树,因为子树顺序的问题,我们可以钦定这棵新子树的根是所有子树的根中最先添加的,那么也就是把剩下k1个元素的序列插进去组成一个i2的序列(整颗树的根和该子树的根已经确定),转移方程就是:

fi,j=k=1i1(i2k1)fik,jd>jfk,d

后面那个求和用一下前缀和就能优化到O(n2K)了。
代码:

#include<iostream>
#include<cstdio>
#define ll long long
#define N 310
using namespace std;
ll C[N][N],f[N][N],s[N][N];
int n,K,mod;
int main()
{
    scanf("%d%d%d",&n,&K,&mod); 
    C[0][0]=1;
    for(int i=1;i<=n;C[i][0]=1,i++)
        for(int j=1;j<=i;j++)
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    for(int j=0;j<=K;j++)
        f[1][j]=1,s[1][j]=K-j+1;
    for(int i=2;i<=n+1;i++)
    {
        for(int j=0;j<=K;j++)
            for(int k=1;k<i;k++)
                f[i][j]=(f[i][j]+f[i-k][j]*s[k][j+1]%mod*C[i-2][k-1])%mod;  
        for(int j=K;j>=0;j--)
            s[i][j]=(f[i][j]+s[i][j+1])%mod;
    }
    printf("%lld",f[n+1][0]);           
    return 0;
}
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页