Luogu P6292 区间本质不同子串个数
给定字符串
, 次询问 中本质不同的字符串个数。
。
考虑将询问离线,右端点扫描线,维护
考虑暴力怎么维护
建出
可以用 LCT 将
Code
#include <bits/stdc++.h>
const int M = 4e5 + 10;
int read() {
char ch = getchar();
int x = 0;
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
return x;
}
int n, m;
char str[M];
struct Q { int l, r, id; } q[M];
long long ans[M];
namespace Tree {
// f[i]: 以 i 开头的,[i, r] 中仅出现一次的字符串个数
struct Node { int l, r; long long sum, lzy; } tr[M << 2];
void pushup(int x) { tr[x].sum = tr[x << 1].sum + tr[x << 1 | 1].sum; }
void tag(int x, long long v) { tr[x].lzy += v, tr[x].sum += 1ll * (tr[x].r - tr[x].l + 1) * v; }
void pushdown(int x) {
if (tr[x].lzy != 0)
tag(x << 1, tr[x].lzy), tag(x << 1 | 1, tr[x].lzy), tr[x].lzy = 0;
}
void build(int x, int l, int r) {
tr[x].l = l, tr[x].r = r;
if (l == r) return;
int mid = (l + r) >> 1;
build(x << 1, l, mid), build(x << 1 | 1, mid + 1, r);
}
void add(int x, int l, int r, int v) {
if (l > r) return;
if (l <= tr[x].l && tr[x].r <= r) return tag(x, v);
pushdown(x);
int mid = (tr[x].l + tr[x].r) >> 1;
if (l <= mid) add(x << 1, l, r, v);
if (r > mid) add(x << 1 | 1, l, r, v);
pushup(x);
}
long long qry(int x, int l, int r) {
if (l <= tr[x].l && tr[x].r <= r) return tr[x].sum;
pushdown(x);
int mid = (tr[x].l + tr[x].r) >> 1;
long long ret = 0;
if (l <= mid) ret += qry(x << 1, l, r);
if (r > mid) ret += qry(x << 1 | 1, l, r);
return ret;
}
}
struct SAM_Node { int ch[26], fa, len; } v[M];
int last = 1, tot = 1, ep[M];
void Insert(int x) {
int cur = ++tot, p = last;
v[cur].len = v[p].len + 1;
while (p && !v[p].ch[x]) v[p].ch[x] = cur, p = v[p].fa;
if (!p) v[cur].fa = 1;
else {
int q = v[p].ch[x];
if (v[q].len == v[p].len + 1) v[cur].fa = q;
else {
int nq = ++tot;
v[nq] = v[q], v[nq].len = v[p].len + 1;
while (p && v[p].ch[x] == q) v[p].ch[x] = nq, p = v[p].fa;
v[q].fa = v[cur].fa = nq;
}
}
last = cur;
}
struct LCT_Node { int ch[2], fa, lzy, lst; } tr[M];
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
bool isRoot(int x) { return lc(tr[x].fa) != x && rc(tr[x].fa) != x; }
bool get(int x) { return rc(tr[x].fa) == x; }
void tag(int x, int v) { tr[x].lzy = tr[x].lst = v; }
void pushdown(int x) {
if (tr[x].lzy != 0) {
if (lc(x)) tag(lc(x), tr[x].lzy);
if (rc(x)) tag(rc(x), tr[x].lzy);
tr[x].lzy = 0;
}
}
void push(int x) {
if (!isRoot(x)) push(tr[x].fa);
pushdown(x);
}
void rotate(int x) {
int y = tr[x].fa, z = tr[y].fa;
int k = get(x), w = tr[x].ch[k ^ 1];
if (!isRoot(y)) tr[z].ch[get(y)] = x;
tr[x].fa = z, tr[x].ch[k ^ 1] = y, tr[y].fa = x, tr[y].ch[k] = w;
if (w) tr[w].fa = y;
}
void splay(int x) {
for (push(x); !isRoot(x); rotate(x))
if (!isRoot(tr[x].fa)) rotate(get(x) ^ get(tr[x].fa) ? x : tr[x].fa);
}
void access(int x, int i) {
int y;
for (y = 0; x; y = x, x = tr[x].fa) {
splay(x), rc(x) = y;
if (tr[x].lst) {
int l = tr[x].lst - v[x].len + 1, r = tr[x].lst - v[tr[x].fa].len;
Tree::add(1, l, r, -1);
}
}
tag(y, i), Tree::add(1, 1, i, 1);
}
int main() {
scanf(" %s", str + 1), n = strlen(str + 1);
for (int i = 1; i <= n; i++) Insert(str[i] - 'a'), ep[i] = last;
for (int i = 1; i <= tot; i++) tr[i].fa = v[i].fa;
Tree::build(1, 1, n);
m = read();
for (int i = 1; i <= m; i++) q[i] = { read(), read(), i };
std::sort(q + 1, q + 1 + m, [&](Q A, Q B) { return A.r < B.r; } );
for (int i = 1; i <= m; i++) {
for (int j = q[i - 1].r + 1; j <= q[i].r; j++) access(ep[j], j);
ans[q[i].id] = Tree::qry(1, q[i].l, q[i].r);
}
for (int i = 1; i <= m; i++) printf("%lld\n", ans[i]);
return 0;
}
作者:zzxLLL
出处:https://www.cnblogs.com/zzxLLL/p/17871169.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话