zoukankan      html  css  js  c++  java
  • 【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;
    }
    
  • 相关阅读:
    XML(学习笔记)
    css样式学习笔记
    Request(对象)
    sql一些错误修改的总结
    转载(如何学习C#)
    sql server(学习笔记2 W3Cschool)
    sql sqrver(学习笔记1 W3Cschool)
    关于 flutter开发碰到的各种问题,有的已经解决有的一直没解决或者用其他方法替代
    关于 Flutter IOS build It appears that your application still contains the default signing identifier.
    关于 flutter本地化问题 The getter 'pasteButtonLabel' was called on null
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/luogu_CF1137F.html
Copyright © 2011-2022 走看看