• 【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;
    }
    
  • 相关阅读:
    mongodb安装
    node版本的管理 n
    npm 命令
    nodejs,npm安装(ubuntu14.04下)
    yeoman,grunt,bower安装(ubuntu14.04)
    什么是堆和栈,它们在哪儿?
    malloc函数详解 (与new对比)
    单链表的C++实现(采用模板类)
    短信验证码
    webapi
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBTOJ_GXJJ_21168.html
Copyright © 2020-2023  润新知