Chain Contestant 题解
前言
最慢的点才跑
题意简述
给定长度为
(嗯……其实原题
题目分析
一眼状压 DP。因为我们需要知道之前选过的字符集合,以及当前上一位选出来的字符是什么。所以我们考虑记
-
当前字符是子序列第一位:
直接 。 -
当前字符不是子序列第一位:
那么肯定是从 拼接来的。枚举 处选出的字符集合 ,根据上一次选出的最后一位 ,结合题目要求有如下转移:解释一下吧,第一种情况是直接接在
之后,第二种情况是新开一段。
答案就是
这样,时间复杂度是 菜爆了,无论时空都不优秀。优化思路是先把
类似 01 背包,我们每次需要让
:
表示选出 ,有 种方案,又由于 被滚了,是类似前缀和的操作,所以累加上去。 :
表示新开一段,其中 。
注意到,我们还有把当前字符选出来成为第一位这一操作,由于滚动数组,我们不能再上述操作前修改,而是再上述操作后使
统计答案显然是
时间复杂度优化到
发现瓶颈在枚举
答案便是
时间复杂度
发现,枚举对于每一个包含
当然,聪明的读者肯定会发现,空间也可以有一个
代码
快到飞起的提交记录。为了观感,以下是没有卡空间的代码。
#include <cstdio> using namespace std; using mint = int; using sint = long long; constexpr const mint mod = 998244353; constexpr inline mint add(const mint a, const mint b) { return a + b >= mod ? a + b - mod : a + b; } template <typename... Types> inline mint& toadd(mint &a, Types... b) { return a = add(a, b...); } const int N = 1010, M = 10; int n, val[N]; char str[N]; mint dp[1 << M][M], g[1 << M]; signed main() { scanf("%d%s", &n, str + 1); for (int i = 1; i <= n; ++i) val[i] = str[i] - 'A'; dp[1 << val[1]][val[1]] = 1; g[1 << val[1]] = 1; for (int i = 2; i <= n; ++i) { int ST = ((1 << M) - 1) ^ 1 << val[i]; for (int S = ST; ; S = (S - 1) & ST) { int st = S | 1 << val[i]; toadd(g[st], dp[st][val[i]]); toadd(g[st], g[st ^ 1 << val[i]]); toadd(dp[st][val[i]], dp[st][val[i]]); toadd(dp[st][val[i]], g[st ^ 1 << val[i]]); if (!S) break; } toadd(dp[1 << val[i]][val[i]], 1); toadd(g[1 << val[i]], 1); } mint ans = 0; for (int st = 0; st < 1 << M; ++st) toadd(ans, g[st]); printf("%d", ans); return 0; }
后记
一道大水题,但是完全可以优化,别的题解都太劣了。以及本来想申请撤下一篇题解的,但是发现他已经被禁用专栏了,乐。
希望我的题解能给大家带来启示和帮助。
本文作者:XuYueming,转载请注明原文链接:https://www.cnblogs.com/XuYueming/p/18374856。
若未作特殊说明,本作品采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现