AGC056B Range Argmax 题解
看数据范围可以大致确定是区间dp。
设 表示只考虑区间 的方案数(只考虑区间 是指只考虑 的 方案数)。
转移枚举 中最大的 即可。但是这样会导致一个问题:不同的 可能对应相同的 。
我们不妨规定, 相同的不同 排列,只计算最大值出现的那个位置最靠前的一个,最大值出现位置相同的只计算次大值出现的位置最靠前的一个……实际上就是把最大值,次大值,次次大值一直到最小值的出现位置保留字典序最小的那个。
对于转移,假设最大值在 位置, 区间没有影响,但假设 区间的最大值在 位置,如果没有跨过 区间且在 以内的线段(这里的线段指每对 )的话,我们可以交换 的值做到更小的字典序。这就给了 区间的最大值位置一个下限。
因此重新设计状态 表示只考虑区间 ,最大值位置 的方案数。为了转移,令 表示 区间内,横跨 区间的线段中左端点最小的那一个。
总结:这道题告诉我们,在需要对某个序列 进行计数时,如果直接计数比较困难但容易对另一个序列 进行计数,且多个 序列可能对应一个 时,可以考虑给 加一些限制,常见的如字典序最小。
其实这个思想不一定只在序列上有用,但是应该多数都是在序列上的吧
#include <cstdio>
#include <cstring>
const int mod = 998244353;
int dp[305][305][305], s[305][305][305];
inline int min(const int x, const int y) {return x < y ? x : y;}
int main() {
int n, m;
scanf("%d%d", &n, &m);
memset(s, 0x3f, sizeof s);
for (int i = 1, l, r; i <= m; ++ i) {
scanf("%d%d", &l, &r);
for (int j = l; j <= r; ++ j) s[l][r][j] = min(s[l][r][j], l);
}
for (register int i = n; i; -- i)
for (register int j = i + 1; j <= n; ++ j)
for (register int k = i; k <= j; ++ k)
s[i][j][k] = min(j + 1, min(s[i][j][k], min(s[i][j - 1][k], s[i + 1][j][k])));
for (int i = 1; i <= n; ++ i) dp[i][i][i] = 1;
for (int i = 1; i <= n + 1; ++ i)
for (int j = 1; j <= n + 1; ++ j) dp[i][i - 1][j] = 1;
for (register int l = n; l; -- l)
for (register int r = l + 1; r <= n; ++ r)
for (register int k = r; k >= l; -- k)
dp[l][r][k] = (dp[l][r][k + 1] + 1ll * dp[l][k - 1][s[l][r][k]] * dp[k + 1][r][k + 1]) % mod;
printf("%d", dp[1][n][1]);
return 0;
}
本文作者:zqs2020
本文链接:https://www.cnblogs.com/stinger/p/15859464.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?