【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
", ans);
		}
	}
	
	return 0;
}
原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBTOJ_GXJJ_21168.html