洛谷P6292 区间本质不同子串个数
解题思路
简单卸卸,将询问离线下来,像HH的项链一样对于每一个子串记录其出现的最右位置,通过后缀自动机可以将过多的子串压起来,但暴力修改颜色肯定不行,用 lct 像树点涂色一样维护即可
代码
#include <queue>
#include <vector>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MP make_pair
#define ll long long
#define fi first
#define se second
using namespace std;
template <typename T>
void read(T &x) {
x = 0; bool f = 0;
char c = getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
for (;isdigit(c);c=getchar()) x=x*10+(c^48);
if (f) x=-x;
}
template<typename F>
inline void write(F x)
{
static short st[30];short tp=0;
if(x<0) putchar('-'),x=-x;
do st[++tp]=x%10,x/=10; while(x);
while(tp) putchar('0'|st[tp--]);
putchar('
');
}
template <typename T>
inline void Mx(T &x, T y) { x < y && (x = y); }
template <typename T>
inline void Mn(T &x, T y) { x > y && (x = y); }
const int N = 600500;
int fa[N], len[N], pos[N];
int ch[N][26], cnt = 1, las = 1;
int add(int c) {
int p = las, np = las = ++cnt;
len[np] = len[p] + 1;
for (; p && !ch[p][c]; p = fa[p]) ch[p][c] = np;
if (!p) return fa[np] = 1, np;
int q = ch[p][c];
if (len[q] == len[p] + 1) return fa[np] = q, np;
int nq = ++cnt; fa[nq] = fa[q];
memcpy(ch[nq], ch[q], sizeof(ch[q]));
fa[q] = fa[np] = nq, len[nq] = len[p] + 1;
for (; ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
return np;
}
ll d1[N], d2[N], m, n;
void add(int x, ll v1, ll v2) {
for (; x <= n; x += x & -x)
d1[x] += v1, d2[x] += v2;
}
ll query(int x) {
ll r1 = 0, r2 = 0;
for (int t = x; t; t -= t & -t)
r1 += d1[t], r2 += d2[t];
return r1 * (x + 1) - r2;
}
void add(int l, int r, ll d) { add(l, d, d * l), add(r + 1, -d, -d * (r + 1)); }
ll query(int l, int r) { return query(r) - query(l - 1); }
struct LCT {
#define ls son[x][0]
#define rs son[x][1]
int son[N][2], f[N], tag[N];
bool nroot(int x) {
return son[f[x]][0] == x || son[f[x]][1] == x;
}
void rotate(int x) {
int y = f[x], z = f[y];
int k = son[y][1] == x, w = son[x][!k];
if (nroot(y)) son[z][son[z][1]==y] = x; f[x] = z;
if (w) f[w] = y; son[y][k] = w;
f[y] = x, son[x][!k] = y;
}
void spread(int x) {
if (!tag[x]) return;
tag[ls] = tag[rs] = tag[x];
// tag[x] = 0;
}
int st[N];
void splay(int x) {
int y = x, z = 0; st[++z] = y;
while (nroot(y)) st[++z] = y = f[y];
while (z) spread(st[z--]);
while (nroot(x)) {
y = f[x], z = f[y];
if (nroot(y)) {
if ((son[y][0] == x) ^ (son[z][0] == y))
rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x, int tg) {
int y = 0;
for (y = 0; x; x = f[y = x]) {
splay(x); rs = y;
if (tag[x]) {
add(tag[x] - len[x] + 1, tag[x] - len[f[x]], -1), tag[x] = tg;
// write(x);
}
}
splay(y), tag[y] = tg;
// write(y);
add(1, tg, 1);
}
}Lct;
ll ans[N];
vector<pair<int, int> > v[N];
char s[N];
int main() {
scanf ("%s", s + 1), n = strlen(s + 1);
for (int i = 1;i <= n; i++) pos[i] = add(s[i]-'a');
for (int i = 2;i <= cnt; i++) Lct.f[i] = fa[i];
// for (int i = 1;i <= n; i++) write(pos[i]);
read(m);
for (int i = 1, l, r;i <= m; i++)
read(l), read(r), v[r].push_back(MP(i, l));
for (int i = 1;i <= n; i++) {
Lct.access(pos[i], i);
for (auto t: v[i])
ans[t.fi] = query(t.se, i);
}
// puts("");
for (int i = 1;i <= m; i++) write(ans[i]);
return 0;
}
LOJ6041. 「雅礼集训 2017 Day7」事情的相似度
解题思路
题面可以改成“区间最长出现过两个及以上的子串”,见到了也要会做
和上题一样,好像字符串和区间有关的蛤
维护一下每个集合的颜色,然后用线段树更新答案即可