【题解】 QTREE6 LCT维护子树信息 SPOJ16549

Legend

Link \(\textrm{to SPOJ}\)

一棵树,每个点初始为白色。

  • 0 u :询问满足 \(u \to v\) 的路径上的点颜色全部相同的 \(v\) 的数量。

  • 1 u :反转 \(u\) 的颜色(异或 1)。

结点个数与操作小于 \(10^5\)

Editorial

容易想到一个做法是对于每一个点维护以它为 \(\rm{LCA}\) 的极大同色连通块的大小。

修改时候一定是修改一个从当前结点到根的一个连续段,减去相同的数。

不管感觉好像不太好写,于是就看了看题解,发现是个 \(\rm{LCT}\)

因为单次修改会带动一堆相邻的点,很烦。所以考虑把点颜色放到它到它直接祖先的这条边上。

对于根,搞一个虚拟结点当祖先。

维护两棵线段树,一棵只连白色边,一棵只连黑色边。

查询的时候找到当前连通块的 \(\rm{LCA}\)(注意到 \(\rm{LCA}\) 一定是与询问结点的颜色不同的)。

也就是直接 findroot 一下,把这个 \(\rm{LCA}\) 转到 splay 的根。

接着查询这个 \(\rm{LCA}\) 实儿子大小(也就是刚刚 access 上来的那个连通块的大小)就是答案。

Code

有一定细节,十分考验对 \(\rm{LCT}\) 的理解程度。

最主要的是,做任何修改前一定要把它转到根,这样可以有效避免错误。

#include <bits/stdc++.h>

#define debug(...) ;//+fprintf(stderr ,__VA_ARGS__)
#define __FILE(x)\
	freopen(#x".in" ,"r" ,stdin);\
	freopen(#x".out" ,"w" ,stdout)
#define LL long long

using namespace std;

const int MX = 1e5 + 23;

int read(){
	char k = getchar(); int x = 0;
	while(k < '0' || k > '9') k = getchar();
	while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
	return x;
}

int n;
struct LCT{
#define lch(x) ch[x][0]
#define rch(x) ch[x][1]
	int ch[MX][2] ,fa[MX] ,size[MX] ,Vsize[MX];
	LCT(){for(int i = 1 ; i < MX ; ++i) size[i] = 1;}
	int get(int x){return x == rch(fa[x]);}
	int Nroot(int x){return get(x) || x == lch(fa[x]);}
	void pushup(int x){size[x] = size[lch(x)] + size[rch(x)] + Vsize[x] + 1;}
	void rotate(int x){
		int f = fa[x] ,gf = fa[f] ,which = get(x) ,W = ch[x][!which];
		if(Nroot(f)) ch[gf][get(f)] = x;
		ch[x][!which] = f ,ch[f][which] = W;
		if(W) fa[W] = f;
		fa[f] = x ,fa[x] = gf ,pushup(f);
	}
	void splay(int x){
		int f;
		while(Nroot(x)){
			if(Nroot(f = fa[x])) rotate(get(x) == get(f) ? f : x);
			rotate(x);
		}pushup(x);
	}
	void access(int x){
		for(int y = 0 ; x ; x = fa[y = x]){
			splay(x);
			Vsize[x] += size[rch(x)];
			Vsize[x] -= size[rch(x) = y];
			pushup(x);
		}
	}
	int findroot(int x){
		access(x);
		splay(x);
		while(lch(x)) x = lch(x);
		splay(x);
		return x;	
	}
	void link(int x ,int y){
		splay(x);
		access(y); splay(y);
		fa[x] = y;
		Vsize[y] += size[x];
		pushup(y);	
	}
	void cut(int x ,int y){

		access(x);
		splay(y);
		rch(y) = fa[x] = 0;
		pushup(y);
	}
	void output(){
		debug("Tree info:\n");
		for(int i = 1 ; i <= n + 1 ; ++i){
			debug("[%d] fa: %d ch:{%d ,%d} sz: %d vsz %d\n" ,i ,fa[i] ,ch[i][0] ,ch[i][1] ,size[i] ,Vsize[i]);
		}
	}
#undef lch
#undef rch
}T[2];

int head[MX] ,tot = 1;
struct edge{
	int node ,next;
}h[MX << 1];
void addedge(int u ,int v ,int flg = 1){
	h[++tot] = (edge){v ,head[u]} ,head[u] = tot;
	if(flg) addedge(v ,u ,false);
}

int fa[MX] ,c[MX];
void dfs(int x){
	T[c[x]].link(x ,fa[x]);
	debug("FA[%d] = %d\n" ,x ,fa[x]);
	for(int i = head[x] ,d ; i ; i = h[i].next){
		if((d = h[i].node) == fa[x]) continue;
		debug("%d->%d\n" ,x ,d);
		fa[d] = x ,dfs(d);
	}
}

void solve(){
	n = read();
	
	for(int i = 1 ,u ,v ; i < n ; ++i){
		u = read() ,v = read();
		addedge(u ,v);
	}
	fa[1] = n + 1;
	dfs(1);
	// T[0].makeroot(n + 1);
	// T[0].output();
	int Q = read();
	while(Q--){
		int type = read() ,x = read();
		if(type == 0){
			int rt = T[c[x]].findroot(x);
			debug("RT = %d\n" ,rt);
			printf("%d\n" ,T[c[x]].size[rt] - T[c[x]].Vsize[rt] - 1);
		}
		else{
			T[c[x]].cut(x ,fa[x]);
			c[x] ^= 1;
			T[c[x]].link(x ,fa[x]);
		}
		// T[0].output();
		// T[1].output();
	}
}

int main(){
	int T = 1;
	for(int i = 1 ; i <= T ; ++i){
		solve();
	}
	return 0;
}
posted @ 2020-10-30 20:02  Imakf  阅读(142)  评论(0编辑  收藏  举报