首先每次染色都是染一种新颜色,且一定从根开始,所以从根到一个节点的相同颜色都在一段内,不同颜色数即是颜色段数。
所以如果对于每个节点,维护一下它到根节点的不同颜色数,似乎操作2和3都可以求出了。对于操作2,用类似于树上差分的思路,x->y的颜色数=x到根的颜色数+y到根的颜色数-2*lca(x,y)到根的颜色数+1,因为每次都是染新颜色,所以两段的连接处不可能颜色相同,保证这样做是正确的。对于操作3,可以提前求dfs序,然后用线段树区间查询即可。所以线段树上的标号并不对应实际点的序号,而是点的dfs序。
对于操作1如何维护节点到根的颜色数,可以把一条链上节点颜色相同的作为重链,不相同的作轻链。需要动态修改链的轻重,所以想到用lct的access。access向上修改的时候,对于节点p,它原先的右儿子所在子树对应是重链,改成了轻链,就是原先一条的同颜色链被砍成两段,所以该子树上所有点+1;新的右儿子所在子树对应是轻链改成重链,原先两个不同颜色合并为一个,所以该子树上所有点-1。需要注意的是,原树上+1或-1的子树的根不是p的右儿子,是右儿子所在splay的深度最浅的点,这个点才在原树上和p有边。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100010;
struct dfs_order{
int l,r;
}d1[N];
struct edge{
int y,next;
}data[N*2];
struct node1{
int l,r,fa;
}tree[N];
struct node2{
int x,f;
}mem[N*4];
int n,m,num,num1,h[N],f1[N][20],dep[N],tid[N];
inline int read(){
int x=0,f=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return f?-x:x;
}
inline void addedge(int x,int y){
data[++num].y=y;data[num].next=h[x];h[x]=num;
}
void dfs(int u,int f,int d){
f1[u][0]=f;dep[u]=d;
d1[u].l=++num1;tid[num1]=u;
tree[u].l=tree[u].r=0;tree[u].fa=f1[u][0];
for(int i=1;i<=17;i++)f1[u][i]=f1[f1[u][i-1]][i-1];
for(int i=h[u];i!=-1;i=data[i].next){
int v=data[i].y;
if(v!=f)dfs(v,u,d+1);
}
d1[u].r=num1;
}
inline void new2(int p,int x){
mem[p].x+=x;mem[p].f+=x;
}
inline void pushdown2(int p){
if(mem[p].f!=0){
new2(p<<1,mem[p].f);new2(p<<1|1,mem[p].f);
mem[p].f=0;
}
}
inline void update2(int p){
mem[p].x=max(mem[p<<1].x,mem[p<<1|1].x);
}
void build(int p,int a,int b){
mem[p].f=0;
if(a==b){mem[p].x=dep[tid[a]];return;}
int mid=(a+b)>>1;
build(p<<1,a,mid);build(p<<1|1,mid+1,b);
update2(p);
}
int query(int p,int pa,int pb,int a,int b){
if(a<=pa&&pb<=b)return mem[p].x;
pushdown2(p);
int mid=(pa+pb)>>1,q1=0,q2=0;
if(a<=mid)q1=query(p<<1,pa,mid,a,b);
if(mid<b)q2=query(p<<1|1,mid+1,pb,a,b);
update2(p);
return max(q1,q2);
}
void add(int p,int pa,int pb,int a,int b,int x){
if(a<=pa&&pb<=b){new2(p,x);return;}
pushdown2(p);
int mid=(pa+pb)>>1;
if(a<=mid)add(p<<1,pa,mid,a,b,x);
if(mid<b)add(p<<1|1,mid+1,pb,a,b,x);
update2(p);
}
inline bool nroot(int p){
return tree[tree[p].fa].l==p||tree[tree[p].fa].r==p;
}
inline void left_rotate(int p){
int q=tree[p].fa,r=tree[q].fa;
tree[q].r=tree[p].l;
if(tree[p].l)tree[tree[p].l].fa=q;
if(nroot(q)){
if(tree[r].l==q)tree[r].l=p;
else tree[r].r=p;
}
tree[q].fa=p;tree[p].l=q;tree[p].fa=r;
}
inline void right_rotate(int p){
int q=tree[p].fa,r=tree[q].fa;
tree[q].l=tree[p].r;
if(tree[p].r)tree[tree[p].r].fa=q;
if(nroot(q)){
if(tree[r].l==q)tree[r].l=p;
else tree[r].r=p;
}
tree[q].fa=p;tree[p].r=q;tree[p].fa=r;
}
inline bool getlr(int p){
return tree[tree[p].fa].l==p;
}
inline void rotate(int p){
if(getlr(p))right_rotate(p);
else left_rotate(p);
}
inline void splay(int p){
while(nroot(p)){
int q=tree[p].fa;
if(nroot(q)){
if(getlr(p)==getlr(q))rotate(q);
else rotate(p);
}rotate(p);
}
}
inline void access(int p){
for(int x=0,y;p;x=p,p=tree[p].fa){
splay(p);y=tree[p].r;
while(tree[y].l)y=tree[y].l;
if(y)add(1,1,n,d1[y].l,d1[y].r,1);
tree[p].r=x;
while(tree[x].l)x=tree[x].l;
if(x)add(1,1,n,d1[x].l,d1[x].r,-1);
}
}
inline int lca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=17;i>=0;i--)if(dep[f1[x][i]]>=dep[y])x=f1[x][i];
if(x==y)return x;
for(int i=17;i>=0;i--)if(f1[x][i]!=f1[y][i]){x=f1[x][i];y=f1[y][i];}
return f1[x][0];
}
int main(){
n=read();m=read();
num=num1=0;memset(h,-1,sizeof h);
for(int x,y,i=1;i<n;i++){
x=read();y=read();
addedge(x,y);addedge(y,x);
}
dfs(1,0,1);dep[0]=0;build(1,1,n);
for(int q,i=1;i<=m;i++){
q=read();
if(q==1){int x=read();access(x);}
else if(q==2){
int x=read(),y=read(),l=lca(x,y);
printf("%d\n",query(1,1,n,d1[x].l,d1[x].l)+query(1,1,n,d1[y].l,d1[y].l)-2*query(1,1,n,d1[l].l,d1[l].l)+1);
}else{
int x=read();
printf("%d\n",query(1,1,n,d1[x].l,d1[x].r));
}
}
return 0;
}