(蒟蒻只能够想到 3 操作可以用树剖线段树维护ORZ)
对于每个 1 操作 , 都会覆盖当前节点到根节点的所有颜色 , 且新颜色对所有经过操作节点的路径都有贡献。
所以我们维护所有边的两边颜色是否相同 , 易知在不相同情况下这条边有1的贡献 , 在这种情况下我们就能够对 2 操作进行维护了(考虑差分)
在这里我们用 $ D_{i} $ 表示点 i 到根存在多少这样的边 ,则操作 2 的 答案为 $ Ans = D_{x} + D_{y} - 2 * D_{lca(x , y)} + 1 $
接下来考虑如何维护这些边。
我们会发现覆盖当前节点到根节点的操作与 LCT 中的 Access 操作类似 ,
所以在这里我们就可以用轻重边来表示这些不同的边了!
用 Access 来模拟操作 1 过程 , 重边 - > 轻边 则进行子树加 , 轻边 - > 重边 则进行子树减
由于我们只要维护 LCT 中的 Access , 所以只要对Access操作进行一些修改就行了
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int N = 1e5 + 5;
struct Virt{
double x , y;
Virt(double _x = 0.0 , double _y = 0.0):x(_x) , y(_y){}
};
Virt operator + (Virt x , Virt y){return Virt(x.x + y.x , x.y + y.y);}
Virt operator - (Virt x , Virt y){return Virt(x.x - y.x , x.y - y.y);}
Virt operator * (Virt x , Virt y){return Virt(x.x * y.x - x.y * y.y , x.x * y.y + x.y * y.x);}
int read(){
int x = 0 , f = 1; 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 x * f;
}
int n , Q , opt , x , y;
int num , posin[N] , posout[N] , tot , head[N];
int fa[N] , dep[N] , sz[N] , top[N] , son[N] , tree[N];
struct edge{
int pos , nx;
}e[N << 1];
void add(int u , int v){
e[++num].pos = v; e[num].nx = head[u]; head[u] = num;
e[++num].pos = u; e[num].nx = head[v]; head[v] = num;
}
void dfs1(int now){
sz[now] = 1;
for(int i = head[now] ; i ; i = e[i].nx){
if(e[i].pos == fa[now]) continue;
fa[e[i].pos] = now; dep[e[i].pos] = dep[now] + 1;
dfs1(e[i].pos);
sz[now] += sz[e[i].pos];
if(sz[e[i].pos] > sz[son[now]]) son[now] = e[i].pos;
}
}
void dfs2(int now , int father){
posin[now] = ++tot; tree[tot] = now;
top[now] = father;
if(son[now]) dfs2(son[now] , father);
for(int i = head[now] ; i ; i = e[i].nx){
if(e[i].pos == son[now] || e[i].pos == fa[now]) continue;
dfs2(e[i].pos , e[i].pos);
}
posout[now] = tot;
}
int lca(int x , int y){
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]]) swap(x,y);
x = fa[top[x]];
}
return dep[x] < dep[y] ? x : y;
}
struct SegmentTree{
struct node{
int tag , val;
}tr[N << 2];
void paint(int k , int d){
tr[k].tag += d; tr[k].val+=d;
}
void push_down(int k){
paint(k << 1 , tr[k].tag);
paint(k << 1 | 1 , tr[k].tag);
tr[k].tag = 0;
}
void merge(int k){tr[k].val = max(tr[k << 1].val , tr[k << 1 | 1].val);}
void build(int k , int l , int r){
if(l == r) tr[k].val = dep[tree[l]] + 1;
else build(k << 1 , l , (l + r) >> 1) , build(k << 1 | 1 , ((l + r) >> 1) + 1 , r) , merge(k);
}
int query1(int k , int l , int r , int pos){
if(l == r) return tr[k].val;
else{
if(tr[k].tag) push_down(k);
int mid = (l + r) >> 1;
if(mid >= pos) return query1(k << 1 , l , mid , pos);
else return query1(k << 1 | 1, mid + 1 , r , pos);
}
}
int query2(int k , int l , int r , int L , int R){
if(L <= l && r <= R) return tr[k].val;
else{
if(tr[k].tag) push_down(k);
int mid = (l + r) >> 1 , ans = 0;
if(mid >= L) ans = max(ans , query2(k << 1 , l , mid , L , R));
if(mid < R) ans = max(ans , query2(k << 1 | 1 , mid + 1 , r , L , R));
return ans;
}
}
void change(int k , int l , int r , int L , int R , int d){
if(l >= L && r <= R) paint(k , d);
else{
if(tr[k].tag) push_down(k);
int mid = (l + r) >> 1;
if(mid >= L) change(k << 1 , l , mid , L , R , d);
if(mid < R) change(k << 1 | 1 , mid + 1 , r , L , R , d);
merge(k);
}
}
}seg;
void addsub(int k , int d){seg.change(1 , 1 , n , posin[k] , posout[k] , d);}
int subquery(int x , int y){
int temp = lca(x , y);
//printf("lca : %d\n",temp);
int ans = seg.query1(1 , 1 , n , posin[x]) + seg.query1(1 , 1 , n , posin[y]) - 2 * seg.query1(1 , 1 , n , posin[temp]) + 1;
return ans;
}
int maxquery(int k){
int ans = seg.query2(1 , 1 , n , posin[k] , posout[k]);
return ans;
}
struct Link_Cut_Tree{
struct node{int ch[2] , fa;}tr[N];
int get_son(int x){return tr[tr[x].fa].ch[1] == x;}
bool is_root(int x){return tr[tr[x].fa].ch[0] != x && tr[tr[x].fa].ch[1] != x;}
void rotate(int k){
int f = tr[k].fa , ffa = tr[f].fa , c = get_son(k);
if(!is_root(f)) tr[ffa].ch[tr[ffa].ch[1] == f] = k; tr[k].fa = ffa;
tr[f].ch[c] = tr[k].ch[!c]; tr[tr[f].ch[c]].fa = f;
tr[k].ch[!c] = f; tr[f].fa = k;
}
void splay(int x){
for(int f ; !is_root(x) ; rotate(x))
if(!is_root(f = tr[x].fa))
rotate(get_son(x) == get_son(f) ? f : x);
}
int find_root(int x){
splay(x);
while(tr[x].ch[0]) x = tr[x].ch[0];
splay(x);
return x;
}
void access(int x){
for(int y = 0 ; x ; y = x , x = tr[x].fa){
splay(x);
if(tr[x].ch[1]){
int temp = tr[x].ch[1]; tr[x].ch[1] = 0;
temp = find_root(temp); addsub(temp , 1);
}
if(y) y = find_root(y) , addsub(y , -1);
tr[x].ch[1] = y;
}
}
void init(){for(int i = 1 ; i <= n ; i++) tr[i].fa = fa[i];};
}lct;
int main(){
scanf("%d%d",&n,&Q);
for(int i = 1 ; i < n ; i++) scanf("%d%d",&x,&y) , add(x , y);
dfs1(1); dfs2(1 , 1); seg.build(1 , 1 , n); lct.init();
for(int i = 1 ; i <= Q ; i++){
scanf("%d%d",&opt,&x);
if(opt == 1) lct.access(x);
else if(opt == 2) scanf("%d",&y) , printf("%d\n" , subquery(x , y));
else printf("%d\n" , maxquery(x));
}
return 0;
}