CSP-S 2023 消消乐
考虑 dp,设 为以 结尾的合法子串个数。如果我们能对每个 ,求出来 表示最大的左端点 使得 是合法串,那么 。若 不存在则 。答案为 。
考虑求 。因为 是最大的,所以 ,并且 是由若干个 组成的合法串拼接而成。
这启发我们连边 ,转化成求 不断跳出边直到跳到字符 或没有出边,并找到跳到的点。
套路地考虑倍增,设 为点 跳了 次出边后所在的点, 为点 跳了 次出边后,访问过的所有点(包括 ,不包括 跳 次出边后所在的点),出现过的字符集合。由于 ( 为字符集大小),因此可以用一个 int
存储。
那么求 就只用跳到最后一个点 使得从 跳到 都不出现字符 ,那么 往上跳一步就是我们想求的 。
求出来 后,令 ,再更新倍增数组即可。
有若干细节,包括要特判 以及 不存在。
时空复杂度均为 。
事实上这种做法可拓展性不强,由于要维护 只能做 较小的情况,不适用于 CF1223F。
code
#include <bits/stdc++.h> #define pb emplace_back #define fst first #define scd second #define mkp make_pair #define mems(a, x) memset((a), (x), sizeof(a)) using namespace std; typedef long long ll; typedef double db; typedef unsigned long long ull; typedef long double ldb; typedef pair<ll, ll> pii; const int maxn = 2000100; const int logn = 23; int n, f[maxn][logn], g[maxn][logn]; ll h[maxn]; char s[maxn]; void solve() { scanf("%d%s", &n, s + 1); ll ans = 0; for (int i = 1; i <= n; ++i) { if (i > 1 && s[i] == s[i - 1]) { h[i] = h[i - 2] + 1; ans += h[i]; f[i][0] = i - 2; g[i][0] = (1 << (s[i] - 'a')); for (int j = 1; j < 21; ++j) { f[i][j] = f[f[i][j - 1]][j - 1]; g[i][j] = g[i][j - 1] | g[f[i][j - 1]][j - 1]; } continue; } int u = i - 1; for (int j = min(__lg(i) + 1, 20); ~j; --j) { if ((~g[u][j]) & (1 << (s[i] - 'a'))) { u = f[u][j]; } } if (!u) { g[i][0] = (1 << (s[i] - 'a')); for (int j = 1; j < 21; ++j) { f[i][j] = f[f[i][j - 1]][j - 1]; g[i][j] = g[i][j - 1] | g[f[i][j - 1]][j - 1]; } continue; } --u; h[i] = h[u] + 1; ans += h[i]; f[i][0] = u; g[i][0] = (1 << (s[i] - 'a')); for (int j = 1; j < 21; ++j) { f[i][j] = f[f[i][j - 1]][j - 1]; g[i][j] = g[i][j - 1] | g[f[i][j - 1]][j - 1]; } } printf("%lld\n", ans); } int main() { int T = 1; // scanf("%d", &T); while (T--) { solve(); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现