[AGC024] E - Sequence Growing Hard 树型DP

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

fi,j=k=1i1(i2k1)fik,jd>jfk,d f i , j = ∑ k = 1 i − 1 ( i − 2 k − 1 ) f i − k , j ∗ ∑ d > j f k , d

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

#include
#include
#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;k1]%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;
}

你可能感兴趣的:(dp,树)