【题解】 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;
}