题解:P9576 「TAOI-2」Ciallo~(∠・ω< )⌒★
Luogu 专栏:Link。
如果在分割前
接下来我们讨论
首先有一种非常暴力的做法,枚举字符串
举个例子,就针对题目中第一个样例,我们如果将
那么它在
其实这就相当于在
这样做总复杂度是
观察上述做法,发现它依赖于分割点,这样复杂度一定有一个枚举分割点的
不妨换个角度入手,观察上面的例子,你会发现红色部分(就是
仔细观察上面的例子与它的性质,将它刻画成更一般的形式。这样,你会发现,合法的情况都形如下图:
这里有两个性质,等会会用到:
的前后缀不能离得太近,要不然中间没有可以切开的地方。具体地,它们(端点)的距离要大于 。- 红色部分的长度与蓝色部分的长度之和等于
。
还是上面那个图,现在,我们固定这个前缀和后缀。记
你会发现,红色部分不超过
也就是说,一个
考虑优化。根据性质一,我们可以双指针枚举前缀和后缀。观察一个
求
当然,你也可以把上面的限制刻画成一个二元偏序关系,然后直接二维数点解决。
代码:
#include <bits/stdc++.h>
#define int long long
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
const int N = 1e6 + 10;
typedef long long ll;
int n, m;
char a[N], b[N];
int f[N], p[N], s[N];
int ans = 0;
struct tree{
int l, r;
int val, lzy;
}t[N << 2];
void pushup(int u) {
t[u].val = t[ls].val + t[rs].val;
}
void maketag(int u, int x) {
t[u].val += (t[u].r - t[u].l + 1) * x;
t[u].lzy += x;
}
void pushdown(int u) {
if (!t[u].lzy) return ;
maketag(ls, t[u].lzy);
maketag(rs, t[u].lzy);
t[u].lzy = 0;
}
void build(int u, int l, int r) {
t[u].l = l, t[u].r = r;
if (l == r) return ;
int M = (l + r) >> 1;
build(ls, l, M);
build(rs, M + 1, r);
pushup(u);
}
void modify(int u, int l, int r, int x) {
if (l <= t[u].l && t[u].r <= r) maketag(u, x);
else {
int M = (t[u].l + t[u].r) >> 1;
pushdown(u);
if (l <= M) modify(ls, l, r, x);
if (r > M) modify(rs, l, r, x);
pushup(u);
}
}
int query(int u, int l, int r) {
if (l <= t[u].l && t[u].r <= r) return t[u].val;
int M = (t[u].l + t[u].r) >> 1, res = 0;
pushdown(u);
if (l <= M) res += query(ls, l, r);
if (r > M) res += query(rs, l, r);
pushup(u);
return res;
}
signed main() {
cin >> a + 1 >> b + 1;
n = strlen(a + 1), m = strlen(b + 1);
b[m + 1] = '*';
for (int i = m + 2; i <= m + n + 1; i++) b[i] = a[i - m - 1];
int k1 = 0, k2 = 0;
f[1] = m;
for (int i = 2; i <= m + n + 1; i++) {
if (k2 >= i) f[i] = min(k2 - i + 1, f[i - k1 + 1]);
while (i + f[i] <= n + m + 1 && b[1 + f[i]] == b[i + f[i]]) f[i]++;
if (i + f[i] - 1 >= k2) k1 = i, k2 = i + f[i] - 1;
}
for (int i = m + 2; i <= m + n + 1; i++) p[i - m - 1] = f[i];
reverse(a + 1, a + n + 1);
reverse(b + 1, b + m + 1);
for (int i = m + 2; i <= m + n + 1; i++) b[i] = a[i - m - 1];
memset(f, 0, sizeof f);
k1 = 0, k2 = 0;
f[1] = m;
for (int i = 2; i <= m + n + 1; i++) {
if (k2 >= i) f[i] = min(k2 - i + 1, f[i - k1 + 1]);
while (i + f[i] <= n + m + 1 && b[1 + f[i]] == b[i + f[i]]) f[i]++;
if (i + f[i] - 1 >= k2) k1 = i, k2 = i + f[i] - 1;
}
for (int i = m + 2; i <= m + n + 1; i++) s[i - m - 1] = f[i];
reverse(s + 1, s + n + 1);
build(1, 1, n);
for (int i = 1; i <= n; i++) {
int j = m + i;
if (j > n) break;
if (p[i]) modify(1, 1, p[i], 1);
if (s[j]) ans += query(1, max(1ll, m - s[j]), m - 1);
}
for (int i = 1; i <= n; i++) {
if (p[i] < m) continue;
int l = i - 1, r = i + p[i];
if (l >= 1) ans += l * (l + 1) / 2;
if (r <= n) ans += (n - r + 2) * (n - r + 1) / 2;
}
cout << ans << "\n";
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现