CSP-S 2023 消消乐

洛谷传送门

考虑 dp,设 fi 为以 i 结尾的合法子串个数。如果我们能对每个 i,求出来 gi 表示最大的左端点 l 使得 [l,i]合法串,那么 fi=fgi1+1。若 gi 不存在则 fi=0。答案为 i=1nfi

考虑求 gi。因为 gi最大的,所以 sgi=si,并且 [gi+1,i1] 是由若干个 [gj,j] 组成的合法串拼接而成。

这启发我们连边 igi1,转化成求 i 不断跳出边直到跳到字符 si 或没有出边,并找到跳到的点。

套路地考虑倍增,设 Fi,j 为点 i 跳了 2j 次出边后所在的点,Gi,j 为点 i 跳了 2j 次出边后,访问过的所有点(包括 i,不包括 i2j 次出边后所在的点),出现过的字符集合。由于 |Σ|=26|Σ| 为字符集大小),因此可以用一个 int 存储。

那么求 gi 就只用跳到最后一个j 使得从 i1 跳到 j 都不出现字符 si,那么 j 往上跳一步就是我们想求的 gi

求出来 gi 后,令 Fi,0=gi1,Gi,0={si},再更新倍增数组即可。

有若干细节,包括要特判 si1=si 以及 gi 不存在。

时空复杂度均为 O(nlogn)

事实上这种做法可拓展性不强,由于要维护 G 只能做 |Σ| 较小的情况,不适用于 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;
}
posted @   zltzlt  阅读(179)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示