【ybtoj高效进阶 21168】打字机器(Trie树)(LCA)(值域线段树)
打字机器
题目链接:ybtoj高效进阶 21168
题目大意
要你实现一些操作:
在末尾加字符或删字符,记录当前的字符串。
将记录的第 i 个字符串的第 j 个字符设为不可匹配字符,即它不可以与任何字符匹配,两个不可匹配字符和不可以匹配。(如果这个位置原来就是,就改回之前的字符)
求两个记录下来的字符串的最长公共前缀。
思路
首先是找到那些记录的字符串,这个可以用 Trie 树很快的实现。
然后你考虑匹配,如果没有不可匹配字符。
那就是直接是 Trie 树上的 LCA 的深度。
然后就是有不可匹配字符,那就是两个字符串分别找到它们第一个不可匹配字符(如果没有就是在最后一个)
现在一想好像可删堆(支持删除的单调栈)这些都可以,但我当时用了值域线段树。
然后就好啦。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
struct Trie {
int son[31], fa[22], deg;
}a[500001];
int n, op, x, y, tot, pls[500001], now;
int b[500001], bn, opp[500001], c[500001];
char cc;
int LCA(int x, int y) {
if (a[x].deg < a[y].deg) swap(x, y);
for (int i = 20; i >= 0; i--)
if (a[a[x].fa[i]].deg >= a[y].deg)
x = a[x].fa[i];
if (x == y) return x;
for (int i = 20; i >= 0; i--)
if (a[x].fa[i] != a[y].fa[i])
x = a[x].fa[i], y = a[y].fa[i];
return a[x].fa[0];
}
struct XDtree {//值域线段树
int ls[500001 << 6], rs[500001 << 6], tott, root[500001];
bool a[500001 << 6];
void up(int now) {
if (ls[now] && a[ls[now]]) {a[now] = 1; return ;}
if (rs[now] && a[rs[now]]) {a[now] = 1; return ;}
a[now] = 0;
}
int insert(int now, int l, int r, int pl) {
if (!now) now = ++tott;
if (l == r) {
a[now] ^= 1;
return now;
}
int mid = (l + r) >> 1;
if (pl <= mid) ls[now] = insert(ls[now], l, mid, pl);
else rs[now] = insert(rs[now], mid + 1, r, pl);
up(now);
return now;
}
int get_fir(int now, int l, int r) {
if (!now) return -1;
if (!a[now]) return -1;
if (l == r) return l;
int mid = (l + r) >> 1;
int x = get_fir(ls[now], l, mid);
if (x == -1) return get_fir(rs[now], mid + 1, r);
else return x;
}
}T;
int main() {
// freopen("typewriter.in", "r", stdin);
// freopen("typewriter.out", "w", stdout);
// printf("%.3lf", (sizeof(a) + sizeof(pls) * 5 + sizeof(T)) / 1024.0 / 1024.0);
scanf("%d", &n);
now = 1; tot = 1;
while (n--) {
scanf("%d", &op);
if (op == 1) {
cc = getchar();
while (cc < 'a' || cc > 'z') cc = getchar();
if (a[now].son[cc - 'a']) now = a[now].son[cc - 'a'];
else {//建 Trie 树
a[now].son[cc - 'a'] = ++tot;
a[tot].fa[0] = now;
a[tot].deg = a[now].deg + 1;
now = tot;
}
}
if (op == 2) {
now = a[now].fa[0];
}
if (op == 3) {
b[++bn] = now;
opp[bn] = 3;
}
if (op == 4) {
scanf("%d %d", &x, &y);
b[++bn] = x; c[bn] = y;
opp[bn] = 4;
}
if (op == 5) {
scanf("%d %d", &x, &y);
b[++bn] = x; c[bn] = y;
opp[bn] = 5;
}
}
for (int i = 1; i <= 20; i++)//建 Trie 树上的倍增数组,准备 LCA
for (int j = 1; j <= tot; j++)
a[j].fa[i] = a[a[j].fa[i - 1]].fa[i - 1];
for (int i = 1; i <= bn; i++) {
if (opp[i] == 3) {
pls[++pls[0]] = b[i];
}
if (opp[i] == 4) {
T.root[b[i]] = T.insert(T.root[b[i]], 1, a[pls[b[i]]].deg, c[i]);
}
if (opp[i] == 5) {
int ans = a[LCA(pls[b[i]], pls[c[i]])].deg;//如果没有不可匹配就是 LCA 的深度
int x = T.get_fir(T.root[b[i]], 1, a[pls[b[i]]].deg);//找到两边早的不可匹配
if (x == -1) x = a[pls[b[i]]].deg + 1;
int y = T.get_fir(T.root[c[i]], 1, a[pls[c[i]]].deg);
if (y == -1) y = a[pls[c[i]]].deg + 1;
ans = min(ans, min(x, y) - 1);
printf("%d\n", ans);
}
}
return 0;
}