【SPOJ16549】QTREE6 - Query on a tree VI
LCT。
题目大意:
给定一棵树,每个结点为黑色或白色,初始为同一种颜色。有两个操作:
- 给定 $u$,询问 $u$ 所在的同色连通块的大小。
- 给定 $u$,翻转 $u$ 的颜色。
题解:
比较容易想到的是对两种颜色分别用 LCT 维护树的结构,存储子树大小之类的。但修改一次颜色会造成 $O(n)$ 条边的变动(比如一张菊花图),复杂度不正确。
一个挺妙的 Trick。
我们考虑修改一个点的颜色的时候,只修改这个点连向父亲的边。
然后考虑一次询问,这个点所在的连通块可能有两种情况:颜色全部相同;只有根结点颜色不同。
对于第一种情况很好处理。对于第二种情况,我们只需要 access 完后,找到根所在的 splay 的右儿子的值即可。
这样每次只会修改 $O(1)$ 条边,复杂度正确。
由于这里树的形态不能改变(否则找不到正确的根,就无法判断不同色的那个点了),所以我们不能使用 makeroot 操作。而又因为树的形态不变,每个点的父亲不变,因此可以避免 makeroot 操作完成 link 和 cut 操作,这样每个结点 findroot 的时候找到的根就是正确的了。
具体细节可参考代码的 LCT 部分。
时间复杂度 $O((n+m)\log n)$。
Code:
#include<iostream>
#include<algorithm>
using namespace std;
const int N=3e5+6;
int n,m,head[N],cnt,fa[N],dep[N];
bool col[N];
struct lct{
int ch[N][2],fa[N],val[N],sz[N],sm[N],_v[N];
bool tag[N];
inline bool ckr(int x,int p=1){return ch[fa[x]][p]==x;}
inline bool isroot(int x){return!(ckr(x)||ckr(x,0));}
inline void update(int x){
sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;
sm[x]=sm[ch[x][0]]+sm[ch[x][1]]+_v[x]+val[x];
}
inline void flip(int x){swap(ch[x][0],ch[x][1]),tag[x]^=1;}
inline void pushdown(int x){
if(tag[x]){
if(ch[x][0])flip(ch[x][0]);
if(ch[x][1])flip(ch[x][1]);
tag[x]=0;
}
}
void rotate(int x){
int y=fa[x],z=fa[y],k=ckr(x);
if(!isroot(y))ch[z][ckr(y)]=x;
fa[x]=z,fa[y]=x,fa[ch[x][!k]]=y;
ch[y][k]=ch[x][!k],ch[x][!k]=y;update(y);
}
void splay(int x){
static int sta[N],top;sta[top=1]=x;
for(int y=x;!isroot(y);sta[++top]=y=fa[y]);
while(top)pushdown(sta[top--]);
for(;!isroot(x);rotate(x))
if(!isroot(fa[x]))rotate((ckr(x)^ckr(fa[x]))?x:fa[x]);
update(x);
}
void access(int x){
for(int t=0;x;_v[x]-=sm[t]-sm[ch[x][1]],ch[x][1]=t,t=x,x=fa[x])splay(x);
}
void makeroot(int x){access(x),splay(x),flip(x);}
void link(int x,int y){splay(x),access(y),splay(y),fa[x]=y,_v[y]+=sm[x],update(y);}
void cut(int x,int y){access(x),splay(y),ch[y][1]=fa[x]=0,update(y);}
int findroot(int x){access(x);for(splay(x);ch[x][0];x=ch[x][0]);splay(x);return x;}
void init(){
for(int i=1;i<=n;++i)sz[i]=sz[i+n]=val[i]=sm[i]=1;
}
int ask(int u,bool cl){
int x=findroot(u);
if(col[x]==cl)return sm[x];
return sm[ch[x][1]];
}
}T[2];
struct edge{
int to,nxt;
}e[N<<1];
void dfs(int now){
for(int i=head[now];i;i=e[i].nxt)
if(!dep[e[i].to]){
dep[e[i].to]=dep[now]+1,fa[e[i].to]=now;
dfs(e[i].to);
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<n;++i){
int u,v;
cin>>u>>v;
e[++cnt]=(edge){v,head[u]},head[u]=cnt;
e[++cnt]=(edge){u,head[v]},head[v]=cnt;
}
dfs(dep[1]=1);
T[0].init(),T[1].init();
for(int i=2;i<=n;++i){
T[0].link(i,i+n);
T[0].link(i+n,fa[i]);
}
for(cin>>m;m--;){
int op,u;
cin>>op>>u;
if(op){
bool&c=col[u];
if(u==1){
c=!c;
continue;
}
T[c].cut(u,u+n);
T[c].cut(u+n,fa[u]);
T[c=!c].link(u,u+n);
T[c].link(u+n,fa[u]);
}else cout<<T[col[u]].ask(u,col[u])<<'\n';
}
return 0;
}