• 【luogu CF1137F】Matches Are Not a Child‘s Play


    Matches Are Not a Child's Play

    题目链接:luogu CF1137F

    题目大意

    定义一个树的序列是每次把权值最小叶节点删去,这个删去的顺序序列。
    然后给你一个树,要你维护三个操作:
    把一个点的权值改成当前树最大权值+1,求一个点在这个序列中的位置,比较两个点在这个序列中谁更靠前。

    思路

    易得第三个问题是来搞笑的,搞出第二个问题就行。

    首先我们考虑求出了一开始的删除序列(这个好求),然后进行修改会怎么变动。
    那你会发现搞到最后会剩下一条链,就是最大和第二大为两端的链。
    然后对于这条链,它会从第二大那一段开始删,一直删,删到最大那边。
    那你发现这段有序,考虑把这一条链提取出来,用 LCT。

    那你考虑把它染成最深的颜色。
    至于怎么染色,就是搞一个懒标记,它的颜色传给它的儿子。
    那我们想到就分出了虚实边,实边是颜色相同的,虚边是颜色不同的。

    它看似要提取链,但其实我们不用,一开始我们建树以 (n) 为根,接着每次就把要修改的点 (i) 弄成根,那每次到根节点的 access 路径就是我们要的路径了。

    那我们考虑对于一个点的删除序列中位置,就是权值比它小的个数加上它所在实链上颜色比它深的个数加一。

    那对于求权值比它小的,我们可以用一个树状数组来搞。
    那我们 access 修改的时候,我们就要先维护一个 (sz) 代表它实子树大小,然后每次减去之前的颜色,加上新的颜色。
    然后置于它所在实链上颜色比它深的,我们就直接反应为在平衡树上它右子树的个数。

    然后就可以了,具体的实现可以看看代码。

    代码

    #include<cstdio>
    #include<algorithm>
    
    using namespace std;
    
    struct node {
    	int to, nxt;
    }e[400001];
    int n, q, l[200001], r[200001], tot;
    int x, y, fa[200001], sz[200001];
    int tree[400001], col[200001];
    int le[200001], KK;
    bool lzs[200001];
    char op;
    
    void add(int x, int y) {
    	e[++KK] = (node){y, le[x]}; le[x] = KK;
    	e[++KK] = (node){x, le[y]}; le[y] = KK;
    }
    
    //树状数组
    void add_(int x, int y) {
    	for (; x <= n + q; x += x & (-x))//记得要预留好新开的颜色位置
    		tree[x] += y;
    }
    
    int query_(int x) {
    	int re = 0;
    	for (; x; x -= x & (-x))
    		re += tree[x];
    	return re;
    }
    
    //LCT
    bool nrt(int now) {
    	return l[fa[now]] == now || r[fa[now]] == now;
    }
    
    bool ls(int now) {
    	return l[fa[now]] == now;
    }
    
    void up(int now) {
    	sz[now] = sz[l[now]] + sz[r[now]] + 1;
    }
    
    void downs(int now) {
    	lzs[now] ^= 1;
    	swap(l[now], r[now]);
    }
    
    void down(int now) {
    	if (l[now]) col[l[now]] = col[now];//颜色的传递
    	if (r[now]) col[r[now]] = col[now];
    	if (lzs[now]) {
    		if (l[now]) downs(l[now]);
    		if (r[now]) downs(r[now]);
    		lzs[now] = 0;
    	}
    }
    
    void down_line(int now) {
    	if (nrt(now)) down_line(fa[now]);
    	down(now);
    }
    
    void rotate(int x) {
    	int y = fa[x];
    	int z = fa[y];
    	int b = (ls(x) ? r[x] : l[x]);
    	if (z && nrt(y)) (ls(y) ? l[z] : r[z]) = x;
    	if (ls(x)) r[x] = y, l[y] = b;
    		else l[x] = y, r[y] = b;
    	fa[x] = z;
    	fa[y] = x;
    	if (b) fa[b] = y;
    	
    	up(y);
    }
    
    void Splay(int x) {
    	down_line(x);
    	
    	while (nrt(x)) {
    		if (nrt(fa[x])) {
    			if (ls(x) == ls(fa[x])) rotate(fa[x]);
    				else rotate(x);
    		}
    		rotate(x);
    	}
    	
    	up(x);
    }
    
    void access(int x) {
    	int lst = 0;
    	for (; x; x = fa[x]) {
    		Splay(x);
    		
    		r[x] = 0;
    		up(x);
    		add_(col[x], -sz[x]);//把原来的颜色清掉
    		add_(tot, sz[x]);//染上新的最大颜色
    		
    		r[x] = lst;
    		up(x);
    		lst = x;
    	}
    }
    
    void make_root(int x) {
    	tot++;//新开最大的颜色
    	access(x);
    	Splay(x);
    	col[x] = tot;//只用标记最上面的,后面的当懒标记下传
    	downs(x);
    }
    
    int query(int x) {
    	Splay(x);
    	return query_(col[x] - 1) + sz[r[x]] + 1;
    }
    
    void dfs(int now) {
    	col[now] = now;
    	for (int i = le[now]; i; i = e[i].nxt)
    		if (!col[e[i].to]) {
    			fa[e[i].to] = now;
    			dfs(e[i].to);
    			if (col[e[i].to] > col[now]) {//要删了它才能删儿子
    				col[now] = col[e[i].to];
    				r[now] = e[i].to;
    			}
    		}
    	add_(col[now], 1);
    	up(now);
    }
    
    int main() {
    	scanf("%d %d", &n, &q);
    	
    	for (int i = 1; i < n; i++) {
    		scanf("%d %d", &x, &y);
    		add(x, y);
    	}
    	
    	tot = n;
    	dfs(n);
    	
    	for (int j = 1; j <= q; j++) {
    		op = getchar();
    		while (op != 'u' && op != 'w' && op != 'c') op = getchar();
    		
    		if (op == 'u') {
    			for (int i = 1; i <= 1; i++) getchar();
    			
    			scanf("%d", &x);
    			make_root(x);
    			
    			continue;
    		}
    		if (op == 'w') {
    			for (int i = 1; i <= 3; i++) getchar();
    			
    			scanf("%d", &x);
    			printf("%d
    ", query(x));
    			
    			continue;
    		}
    		if (op == 'c') {
    			for (int i = 1; i <= 6; i++) getchar();
    			
    			scanf("%d %d", &x, &y);
    			printf("%d
    ", (query(x) < query(y)) ? x : y);
    			
    			continue;
    		}
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    react之redux的使用笔记
    react之jsx的使用
    react之第一个组件的定义及使用
    npm
    webpack热加载
    react使用笔记及生命周期
    移动开发的常见问题
    javascript常用的方法
    cordova local notification plugin
    jqmobi 转换语言
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/luogu_CF1137F.html
Copyright © 2020-2023  润新知