传送门

$LCT$维护树上联通块。

首先看$1$操作,起点为根的链染色,而且是chuan全新的颜色。可以自己画个图看看。

是不是有种$LCT$里的$access$的感觉?那就可以考虑用$LCT$维护树的形态,把相同颜色的点放在同一棵$splay$里。因为一定是从根开始的染色,染的是不同的颜色,初始颜色又不同,就能保证一棵$splay$一定维护一条链,符合$LCT$的性质,是可行的。(事实上这也是$LCT$维护树上联通块的常见姿势)

于是对于操作$1$,就是$access$一下。对于一个点的权值也就是从该点到根节点的路径会经过几个$splay$。因为在$access$时,每向上跳一次虚边就相当于跨过了一个$splay$,所以可以用这个性质来统计。

伪代码:

    int fake_access(int x){
        int ans=0;
        for(;x;x=fa[x],++ans)
            splay(x);
        //统计答案不能真的access改变树的形态,所以不连儿子
        //这也是为啥叫fAKe_access——它没有真的access
        return ans;
    }

于是操作$2$就是$fakeaccess(x)+fakeaccess(y)-2*fakeaccess(lca(x,y))+1$

(一定要$+1$,因为减去了$2*fakeaccess(lca(x,y))$后,$lca(x,y)$自己的$splay$就被减没了)

麻烦的是操作$3$。

根据上面,一个点的权值,也就相当于一个点到根节点路径上有几条虚边

那么有一条虚边变重了,它对应的子树权值就会都$-1$;有一条实边变轻了,它对应的子树权值都会$+1$。

初始权值好求,就是点的深度。子树加加减减,还要求子树权值$max$,可以用$dfs$序+线段树来维护。

能改变虚实边的就是$access$操作了。这里还要在$LCT$中要维护一下每个$splay$中深度最小的点(记为$ma[x]$,原因看下面伪代码解释)。

伪代码:

//seg为dfs序,siz为子树大小
void access(int x){
    for(int y=0;x;y=x,x=fa[x]){
        splay(x);        
        if(son(x,1))st.add(seg[ma[son(x,1)]],seg[ma[son(x,1)]]+siz[ma[son(x,1)]]-1,1,cnt,1,1);
        //son(x,1)变轻,子树+1
        //变轻的一定是这棵splay中深度最小的点(毕竟是一条链),但是本身在splay中可能会出现它的根不是深度最小的点,所以要维护一下
        if(y)st.add(seg[ma[y]],seg[ma[y]]+siz[ma[y]]-1,1,cnt,1,-1);
        //y变重,子树-1
        //同理,变重的也是深度最小的点
        son(x,1)=y;
        update(x);
    }
}

这样就实现了用$LCT$维护形态,在线段树上维护信息。其实这样操作$2$也可以用线段树来查了。

时间复杂度:$O(nlog^2n)$

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <set>

#define maxn 100005
#define inf 0x3f3f3f
#define pn putchar('\n')
#define px(x) putchar(x)
#define ps putchar(' ')
#define pd puts("======================")
#define pj puts("++++++++++++++++++++++")

using namespace std;

inline int read(){
    int x=0,y=0;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return y?-x:x;
}
template<typename T>
inline T read(){
    T x=0;
    int y=0;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return y?-x:x;
}
inline int ran(){
    static int seed=45562;
    return seed=(seed*48271LL%2147483647);
}
int h[maxn],num;
struct edge{
    int to,pre;
}e[maxn<<1];
inline void add(int from,int to){
    e[++num].pre=h[from],h[from]=num,e[num].to=to;
}
int pos[maxn],seg[maxn],top[maxn],deep[maxn],fa[maxn],son[maxn],siz[maxn],cnt;
struct Segment_Tree{
    int ma[maxn<<2],tag[maxn<<2];
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
    inline void update(int node){
        ma[node]=max(ma[ls(node)],ma[rs(node)]);    
    }
    inline void datadown(int node,int d){
        ma[node]+=d;
        tag[node]+=d;
    }
    inline void pushdown(int node){
        datadown(ls(node),tag[node]);
        datadown(rs(node),tag[node]);
        tag[node]=0;
    }
    void build(int l,int r,int node){
        if(l==r){
            ma[node]=deep[pos[l]]+1;
            //因为深度是从0开始的,所以要+1
            return;
        }
        int mid=l+r>>1;
        build(l,mid,ls(node));
        build(mid+1,r,rs(node));
        update(node);
    }
    void add(int L,int R,int l,int r,int node,int d){
        if(L<=l&&R>=r){
            datadown(node,d);
            return;
        }
        if(tag[node])pushdown(node);
        int mid=l+r>>1;
        if(L<=mid)add(L,R,l,mid,ls(node),d);
        if(R>mid)add(L,R,mid+1,r,rs(node),d);
        update(node);
    }
    int ask(int L,int R,int l,int r,int node){
        if(L<=l&&R>=r)return ma[node];
        if(tag[node])pushdown(node);
        int mid=l+r>>1,ans=0;
        if(L<=mid)ans=ask(L,R,l,mid,ls(node));
        if(R>mid)ans=max(ans,ask(L,R,mid+1,r,rs(node)));
        return ans;
    }
}st;//线段树
struct Link_Cut_Tree{
    int son[maxn][2],fa[maxn],ma[maxn];
#define son(x,y) son[x][y]
#define whson(x) (son[fa[x]][1]==x)
#define root(x) (son[fa[x]][0]!=x&&son[fa[x]][1]!=x)
    inline void update(int node){
        if(son(node,0))ma[node]=ma[son(node,0)];
        else ma[node]=node;
    }
    inline void addedge(int s,int f,int wh){
        son(f,wh)=s;
        if(s)fa[s]=f;
    }
    inline void zhuan(int x){
        int f=fa[x],gf=fa[f],wh=whson(x);
        fa[x]=gf;
        if(!root(f))son(gf,whson(f))=x;
        addedge(son(x,wh^1),f,wh);
        addedge(f,x,wh^1);
        update(f),update(x);
    }
    void splay(int x){
        int y=fa[x];
        while(!root(x)){
            if(!root(y))
                zhuan(whson(x)^whson(y)?x:y);
            zhuan(x);
            y=fa[x];
        }
    }
    void access(int x){
        for(int y=0;x;y=x,x=fa[x]){
            splay(x);
            if(son(x,1))st.add(seg[ma[son(x,1)]],seg[ma[son(x,1)]]+siz[ma[son(x,1)]]-1,1,cnt,1,1);
            if(y)st.add(seg[ma[y]],seg[ma[y]]+siz[ma[y]]-1,1,cnt,1,-1);
            son(x,1)=y;
            update(x);
        }
    }
    int fake_access(int x){
        int ans=0;
        for(;x;x=fa[x],++ans)
            splay(x);
        return ans;
    }
}lct;//lct
struct Tree_Chain_Split{
    void dfs1(int node=1){
        siz[node]=1;
        lct.ma[node]=node;
        for(register int i=h[node];i;i=e[i].pre){
            int x=e[i].to;
            if(!siz[x]){
                lct.fa[x]=fa[x]=node;
                deep[x]=deep[node]+1;
                dfs1(x);
                if(siz[x]>siz[son[node]])son[node]=x;
                siz[node]+=siz[x];
            }
        }
    }
    void dfs2(int node=1){
        seg[node]=++cnt;
        pos[cnt]=node;
        if(son[node]){
            top[son[node]]=top[node];
            dfs2(son[node]);
            for(register int i=h[node];i;i=e[i].pre){
                int x=e[i].to;
                if(!seg[x])top[x]=x,dfs2(x);
            }
        }
    }
    int lca(int x,int y){
        while(top[x]!=top[y]){
            if(deep[top[x]]<deep[top[y]])swap(x,y);
            x=fa[top[x]];
        }
        return deep[x]<deep[y]?x:y;
    }//用的树剖求lca
}tcs;
int main(){
    int n=read(),m=read();
    for(register int i=1;i<n;++i){
        int a=read(),b=read();
        add(a,b),add(b,a);
    }
    tcs.dfs1(),tcs.dfs2();
    st.build(1,cnt,1);
    while(m--){
        int s=read(),x=read();
        if(s==1)lct.access(x);
        else if(s==2){
            int y=read();
            printf("%d\n",lct.fake_access(x)+lct.fake_access(y)-(lct.fake_access(tcs.lca(x,y))<<1)+1);
        }
        else printf("%d\n",st.ask(seg[x],seg[x]+siz[x]-1,1,cnt,1));
    }
}