AtCoder Grand Contest 024 E - Sequence Growing Hard 动态规划

博客探讨了如何使用动态规划解决AtCoder编程竞赛中的E题——Sequence Growing Hard。题目要求找到满足特定条件的序列组数量,即序列的每个元素在[1, k]范围内,前一个序列是后一个序列的子序列,并且字典序递增。文章分析了序列插入策略,并通过构建特定性质的树来唯一对应序列组,然后采用动态规划方法计算满足条件的树的数量。" 77448136,6849670,Windows环境下使用Composer安装ThinkPHP5教程,"['PHP', '前端开发', '后端开发', 'Composer', 'ThinkPHP', '服务器环境']
摘要由CSDN通过智能技术生成

题意

给出n,k,m,问有多少个序列组 (A0,A1,...,An) ( A 0 , A 1 , . . . , A n ) 满足以下条件:
序列 Ai A i 的长度恰好为 i i
所有元素均在[1,k]的范围内
Ai1 A i − 1 Ai A i 的子序列
Ai A i 的字典序大于 Ai1 A i − 1
答案模 m m 输出。
n,k300

分析

对于一个长度为 n n 的序列,考虑如何在某个元素左边插入一个新元素,使得新序列的字典序大于原来的序列。
假设把x放在某个数 y y 的左边,那么需要满足两个条件中的其中一个:
1)x>y
2)x=y 2 ) x = y 且在该位置后面第一个不等于 x x 要小于x
但不难发现第二个条件其实是没用的,因为若 y=x y = x ,那么我们在 y y 的左边插入x和在 y y 后面第一个不等于x的数左边插入 x x ,得到的序列都是一样的。
所以我们构造新序列就只能在某个小于x的数的左边插入 x x
考虑按照如下方法对一个序列组构造一棵树:
每个点有两个信息:标号和权值。
初始时有一个节点,标号和权值均为0。
当我们在Ai1的基础上构造 Ai A i 时,设在某个数 p p 左边插入了x,新建一个点,标号为 i i ,权值为x。设 p p 是第r次被插入,则新节点的父亲为标号为 r r 的点。
这样我们就把一个序列组和一棵树唯一对应了起来,接下来我们只要统计满足条件的树的数量。
不难发现这棵树需要满足任意节点的标号和权值都需要严格大于其父亲的标号和权值。
那么我们就可以开始dp了。
f[n,x]表示一棵大小为 n n 的树,满足上述条件且根节点的权值为x的方案。
枚举编号为 1 1 的节点所在子树的大小k,可以得到

f[n,x]=y=x+1kf[nk,x]f[k,y]Cl1n2 f [ n , x ] = ∑ y = x + 1 k f [ n − k , x ] ∗ f [ k , y ] ∗ C n − 2 l − 1

用前缀和优化转移可以做到 O(kn2) O ( k n 2 )

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

typedef long long LL;

const int N=305;

int n,k,MOD,f[N][N],s[N][N],c[N][N];

int main()
{
    scanf("%d%d%d",&n,&k,&MOD);
    c[0][0]=1;
    for (int i=1;i<=n;i++)
    {
        c[i][0]=1;
        for (int j=1;j<=i;j++)
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD;
    }
    for (int i=0;i<=k;i++) f[1][i]=1,s[1][i]=k-i+1;
    for (int i=2;i<=n+1;i++)
    {
        for (int j=0;j<=k;j++)
            for (int l=1;l<i;l++)
                (f[i][j]+=(LL)f[i-l][j]*s[l][j+1]%MOD*c[i-2][l-1]%MOD)%=MOD;
        for (int j=k;j>=0;j--) s[i][j]=(s[i][j+1]+f[i][j])%MOD;
    }
    printf("%d",f[n+1][0]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值