原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3637
Query on a tree VI
Description
You are given a tree (an acyclic undirected connected graph) with n nodes. The tree nodes are numbered from 1 to n.
Each node has a color, white or black. All the nodes are black initially.
We will ask you to perfrom some instructions of the following form:
0 u : ask for how many nodes are connected to u, two nodes are connected iff all the node on the path from u to v (inclusive u and v) have a same color.
1 u : toggle the color of u(that is, from black to white, or from white to black).
Input
The first line contains a number n denoted how many nodes in the tree(1 ≤ n ≤ 105). The next n - 1 lines, each line has two numbers (u, v) describe a edge of the tree(1 ≤ u, v ≤ n). The next line contains a number m denoted how many operations we are going to process(1 ≤ m ≤ 105). The next m lines, each line describe a operation (t, u) as we mentioned above(0 ≤ t ≤ 1, 1 ≤ u ≤ n).
Output
For each query operation, output the corresponding result.
Sample Input
5
1 2
1 3
1 4
1 5
3
0 1
1 1
0 1
Sample Output
5
1
题目大意
询问树上同色联通块大小,支持单点反色。
题解
前置知识:维护虚子树大小。
Q t r e e \mathcal{Qtree} Qtree系列感觉要被 A \mathcal{A} A成板子题了。。。
用 L C T \mathcal{LCT} LCT维护联通块(不知道是不是套路),建立黑白两棵 L C T \mathcal{LCT} LCT,当一个点在某一个 L C T \mathcal{LCT} LCT中被“连上”了,就说明这个点是这棵 L C T \mathcal{LCT} LCT的颜色。
注意,这里的“连上”仅仅指该点与原树中的父亲相连,而不是跟它的儿子相连。
为什么用这么奇怪的判定呢?
一个更简单粗暴的办法是,我们直接遍历所有边并砍掉,将其“彻底剥离” L C T \mathcal{LCT} LCT,这个也很好卡,菊花图即可。此时,使用上述判定的优势就显现出来了,因为一个节点指有一条指向父亲的边,这样每次修改我们就只用修改一条边,大大加快了效率。
那么询问如何处理呢?如果采用简单粗暴的办法,我们直接维护虚子树大小,查询时直接提出来就好了。其实我们可以使用相同的做法,特别之处在于,因为这颗子树的跟是不属于联通块的(由上面的判定可知,根节点没有连父边,所以颜色与该 L C T \mathcal{LCT} LCT不同),又因为 L C T \mathcal{LCT} LCT中的 S p l a y \mathcal{Splay} Splay维护的是深度严格单增的一条链,所以,我们只需要走到根节点的右儿子,输出其子树大小即可。
另外,值得注意的一点是,因为我们上述操作都用到了原树中的父子关系,所以我们必须定义有根树,而且 L C T \mathcal{LCT} LCT不能 m a k e r o o t \mathcal{makeroot} makeroot。
代码
不知道为什么 B Z O J \mathcal{BZOJ} BZOJ这么卡常,反正在自家 O J \mathcal{OJ} OJ过了。
#include<bits/stdc++.h>
#define ls son[v][0]
#define rs son[v][1]
#define C col[a]
using namespace std;
const int M=1e6+5,N=M<<1;
int fa[N],n,m;
bool col[N];
vector<int>mmp[M];
struct LCT{
int dad[N],son[N][2],siz[N],tot[N];
LCT(){fill(tot+1,tot+N,1);}
bool notroot(int v){return son[dad[v]][0]==v||son[dad[v]][1]==v;}
void up(int v){tot[v]=tot[ls]+tot[rs]+siz[v]+1;}
void spin(int v)
{
int f=dad[v],ff=dad[f],k=son[f][1]==v,w=son[v][!k];
if(notroot(f))son[ff][son[ff][1]==f]=v;son[v][!k]=f,son[f][k]=w;
if(w)dad[w]=f;dad[f]=v,dad[v]=ff;
up(f);up(v);
}
void splay(int v)
{
int f,ff;
while(notroot(v))
{
f=dad[v],ff=dad[f];
if(notroot(f))spin((son[f][0]==v)^(son[ff][0]==f)?v:f);
spin(v);
}
}
void access(int v){for(int f=0;v;v=dad[f=v])splay(v),siz[v]+=tot[rs],siz[v]-=tot[rs=f];}
int root(int v){access(v);splay(v);while(ls)v=ls;splay(v);return v;}
void link(int v){access(v);splay(v);int f=dad[v]=fa[v];access(f);splay(f);siz[f]+=tot[v];tot[f]+=tot[v];}
void cut(int v){access(v);splay(v);ls=dad[ls]=0;up(v);}
}lct[2];
void in()
{
int a,b;scanf("%d",&n);
for(int i=1;i<n;++i)scanf("%d%d",&a,&b),mmp[a].push_back(b),mmp[b].push_back(a);
}
void dfs(int v,int f)
{
fa[v]=f;int to;
for(int i=mmp[v].size()-1;i>=0;--i){to=mmp[v][i];if(to==f)continue;dfs(to,v);lct[0].link(to);}
}
void ac()
{
int op,a,rt;
dfs(1,n+1);lct[0].link(1);
scanf("%d",&m);
for(int i=1;i<=m;++i)
{
scanf("%d%d",&op,&a);
if(op)lct[C].cut(a),lct[C^=1].link(a);
else rt=lct[C].root(a),printf("%d\n",lct[C].tot[lct[C].son[rt][1]]);
}
}
int main()
{
in();ac();
return 0;
}