题意
有操作
$0$ $u$:询问有多少个节点 $v$ 满足路径 $u$ 到 $v$ 上所有节点(包括)都拥有相同的颜色
$1$ $u$:翻转 $u$ 的颜色
题解
直接用一个 $LCT$ 去暴力删边连边显然会 $T$
那么只有两个颜色的话就可以建两棵 $LCT$ ,观察到每次单点修改颜色时其子树所包含连通块在原颜色树上与其父亲所代表连通块断开,所以可以看作断开与父节点的边(实际上是点化边的思想),那么其它常规操作即可
注意要建个虚拟节点作为根节点的父亲
注意 $0$ 操作询问的输出,详细解释有在代码注释中给出
代码
#include <iostream>
#include <cstdio>
#include <cstring> using namespace std; const int MAXN = 1e06 + ;
const int MAXM = 1e06 + ; struct LinkedForwardStar {
int to; int next;
} ; LinkedForwardStar Link[MAXM << ];
int Head[MAXN]= {};
int size = ; void Insert (int u, int v) {
Link[++ size].to = v;
Link[size].next = Head[u]; Head[u] = size;
} int N, M;
int ances[MAXN]; struct Link_Cut_Tree {
int father[MAXN];
int son[MAXN][];
int subtree[MAXN], virsize[MAXN]; void init () {
for (int i = ; i <= N; i ++)
father[i] = son[i][] = son[i][] = subtree[i] = virsize[i] = ;
}
int isroot (int p) {
return son[father[p]][] != p && son[father[p]][] != p;
}
int sonbel (int p) {
return son[father[p]][] == p;
}
void pushup (int p) {
subtree[p] = subtree[son[p][]] + subtree[son[p][]] + virsize[p] + ;
}
void rotate (int p) {
int fa = father[p], anc = father[fa];
int s = sonbel (p);
son[fa][s] = son[p][s ^ ];
if (son[fa][s])
father[son[fa][s]] = fa;
if (! isroot (fa))
son[anc][sonbel (fa)] = p;
father[p] = anc;
son[p][s ^ ] = fa, father[fa] = p;
pushup (fa), pushup (p);
}
void splay (int p) {
for (int fa = father[p]; ! isroot (p); rotate (p), fa = father[p])
if (! isroot (fa))
sonbel (p) == sonbel (fa) ? rotate (fa) : rotate (p);
}
void Access (int p) {
for (int tp = ; p; tp = p, p = father[p]) {
splay (p);
virsize[p] += subtree[son[p][]];
son[p][] = tp;
virsize[p] -= subtree[son[p][]];
pushup (p);
}
}
int findroot (int p) {
Access (p), splay (p);
while (son[p][])
p = son[p][];
splay (p);
return p;
}
void link (int p) {
int fa = ances[p];
splay (p);
father[p] = fa;
Access (fa), splay (fa);
subtree[fa] += subtree[p], virsize[fa] += subtree[p];
}
void cut (int p) {
Access (p), splay (p);
father[son[p][]] = , son[p][] = ;
pushup (p);
}
} ;
Link_Cut_Tree LCT[]; void DFS (int root, int father) {
ances[root] = father;
LCT[].link (root);
for (int i = Head[root]; i; i = Link[i].next) {
int v = Link[i].to;
if (v == father)
continue;
DFS (v, root);
}
} int Colour[MAXN]= {}; int getnum () {
int num = ;
char ch = getchar (); while (! isdigit (ch))
ch = getchar ();
while (isdigit (ch))
num = (num << ) + (num << ) + ch - '', ch = getchar (); return num;
} int main () {
N = getnum ();
for (int i = ; i <= N; i ++)
LCT[].subtree[i] = LCT[].subtree[i] = ;
for (int i = ; i < N; i ++) {
int u = getnum (), v = getnum ();
Insert (u, v), Insert (v, u);
}
DFS (, N + );
M = getnum ();
for (int Case = ; Case <= M; Case ++) {
int opt = getnum (), p = getnum ();
int col = Colour[p];
if (opt == ) {
int anc = LCT[col].findroot (p);
printf ("%d\n", LCT[col].subtree[LCT[col].son[anc][]]);
// 注意,因为有可能存在两个不连通的连通快在LCT上连通,又在Access后右节点仅包含当前链
// 故需输出右子树信息而并非减一,否则有可能会算上另一个连通块的答案
}
else if (opt == )
LCT[col].cut (p), LCT[Colour[p] ^= ].link (p);
} return ;
} /*
5
1 2
1 3
1 4
1 5
3
0 1
1 1
0 1
*/ /*
5
1 2
2 3
3 4
4 5
3
1 1
1 3
0 1
*/