LOJ 6041 事情的相似度 题解
Statement
先把串 reverse,多次给 \(l,r\),求
\[ \max_{l\le i<j\le r}\{\text{LCP}(i,j)\}
\]
Solution
- \(\text{sqrtlog}\sim\text{sqrt}\):莫队 + 线段树 / 树状数组 / set,用 SA 做
- \(nm/\omega\):bitset 乱搞
- \(\log^2\):SAM + LCT + BIT
在 parent 树上,LCP 等于 LCA 的 len
离线,每次加入右端点就 access 一下,过程中对产生的 LCA 关系进行更新.
- 还有一种做法是在 parent 树上 set 启发式合并,然后二维数点
Code
一遍写完一遍过!太牛了。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, j, k) for (int i = (j); i <= (k); ++i)
#define reo(i, j, k) for (int i = (j); i >= (k); --i)
typedef long long ll;
const int N = 2e5 + 10;
vector<pair<int, int>> Queries[N];
int n, m;
string s;
struct BIT {
int len, mx[N];
BIT(int _len = 0) {
len = _len, memset(mx, 0, sizeof(mx));
}
void Upd(int x, int v) {
for (; x <= len; x += x & -x) mx[x] = max(mx[x], v);
}
int Qry(int x) {
int res = 0;
for (; x; x -= x & -x) res = max(res, mx[x]);
return res;
}
} bit;
namespace SAM {
int sz, cur, last, len[N], link[N], nxt[N][2];
void init() {
link[0] = -1;
}
void extend(int ch) {
cur = ++sz, len[cur] = len[last] + 1;
int p = last;
for (; ~p; p = link[p])
if (!nxt[p][ch]) nxt[p][ch] = cur;
else break;
if (!~p) {
link[cur] = 0;
} else {
int q = nxt[p][ch];
if (len[p] + 1 == len[q]) {
link[cur] = q;
} else {
int copy = ++sz;
link[copy] = link[q], link[q] = link[cur] = copy, len[copy] = len[p] + 1;
nxt[copy][0] = nxt[q][0], nxt[copy][1] = nxt[q][1];
for (; ~p; p = link[p])
if (nxt[p][ch] == q) nxt[p][ch] = copy;
else break;
}
}
last = cur;
}
void Build() {
init();
for (auto ch : s) extend(ch - 48);
bit = BIT(n);
}
}
int now;
namespace LinkCutTree {
int fa[N], ch[N][2], tag[N], val[N];
#define get(u) (u == ch[fa[u]][1])
#define nrt(u) (u == ch[fa[u]][0] || u == ch[fa[u]][1])
void cov(int u, int v) {
if (u) tag[u] = val[u] = v;
}
void down(int u) {
if (tag[u]) cov(ch[u][0], tag[u]), cov(ch[u][1], tag[u]), tag[u] = 0;
}
void Down(int u) {
if (nrt(u)) Down(fa[u]);
down(u);
}
void rot(int u) {
int f = fa[u], g = fa[f], k = get(u);
if (nrt(f)) ch[g][get(f)] = u;
ch[f][k] = ch[u][!k];
if (ch[u][!k]) fa[ch[u][!k]] = f;
ch[u][!k] = f, fa[f] = u, fa[u] = g;
}
void splay(int u) {
for (Down(u); nrt(u); rot(u)) if (nrt(fa[u])) rot(get(u) == get(fa[u]) ? fa[u] : u);
}
void access(int u) {
int v = 0;
for (; u; v = u, u = fa[u]) {
splay(u);
if (v) {
bit.Upd(n - val[u] + 1, SAM::len[u - 1]);
}
ch[u][1] = v;
}
cov(v, now);
}
#undef get
#undef nrt
}
int ans[N];
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
cin >> n >> m >> s;
rep(i, 1, m) {
int l, r;
cin >> l >> r;
Queries[r].push_back(make_pair(l, i));
}
SAM::Build();
rep(i, 1, SAM::sz) LinkCutTree::fa[i + 1] = SAM::link[i] + 1;
int pos = 0;
rep(i, 1, n) {
now = i;
pos = SAM::nxt[pos][s[i - 1] - 48];
LinkCutTree::access(pos + 1);
for (auto qry : Queries[i])
ans[qry.second] = bit.Qry(n - qry.first + 1);
}
rep(i, 1, m) {
cout << ans[i] << '\n';
}
return 0;
}