Solution
注意到题目中有一个非常引人注目的描述: 开始时每个点的颜色都不同; 每次涂的颜色也与之前的不同. 考虑用LCT来维护答案, 每条实边表示两个点有同种颜色; 每条虚边表示两个点有不同种颜色, 那么一条边到根路径上的不同颜色数量就等于向上走时经过的虚边数量 + 1, 因而操作2的处理就显而易见了. 注意到我们还有操作3的子树查询, 考虑到整棵树的形态在操作中并不会发生改变, 因此我们用DFS序 + 线段树维护每个点到根路径上的虚边数量, 每次操作1时access一个点并对路径上的点的子树在线段树中修改即可.
#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
#include <cstring>
#define vector std::vector
#define max std::max
#define swap std::swap
namespace Zeonfai
{
inline int getInt()
{
int a = 0, sgn = 1;
char c;
while(! isdigit(c = getchar())) if(c == '-') sgn *= -1;
while(isdigit(c)) a = a * 10 + c - '0', c = getchar();
return a * sgn;
}
}
const int N = (int)1e5, LOG = 17;
int n, m;
struct segmentTree
{
struct node
{
int tg, mx;
}nd[N << 2];
inline segmentTree() {memset(nd, 0, sizeof(nd));}
void modify(int u, int L, int R, int pos, int x)
{
nd[u].mx = max(nd[u].mx, x);
if(L == R) return;
if(pos <= L + R >> 1) modify(u << 1, L, L + R >> 1, pos, x); else modify(u << 1 | 1, (L + R >> 1) + 1, R, pos, x);
}
inline void modify(int pos, int x)
{
modify(1, 1, n, pos, x);
}
inline void pushDown(int u)
{
nd[u << 1].tg += nd[u].tg; nd[u << 1].mx += nd[u].tg;
nd[u << 1 | 1].tg += nd[u].tg; nd[u << 1 | 1].mx += nd[u].tg;
nd[u].tg = 0;
}
void modify(int u, int curL, int curR, int L, int R, int dlt)
{
if(curL >= L && curR <= R)
{
nd[u].mx += dlt; nd[u].tg += dlt;
return;
}
pushDown(u);
int mid = curL + curR >> 1;
if(L <= mid) modify(u << 1, curL, mid, L, R, dlt);
if(R > mid) modify(u << 1 | 1, mid + 1, curR, L, R, dlt);
nd[u].mx = max(nd[u << 1].mx, nd[u << 1 | 1].mx);
}
inline void modify(int L, int R, int dlt)
{
modify(1, 1, n, L, R, dlt);
}
int query(int u, int curL, int curR, int L, int R)
{
if(curL >= L && curR <= R) return nd[u].mx;
pushDown(u);
int mid = curL + curR >> 1, res = 0;
if(L <= mid) res = max(res, query(u << 1, curL, mid, L, R));
if(R > mid) res = max(res, query(u << 1 | 1, mid + 1, curR, L, R));
return res;
}
inline int query(int L, int R)
{
return query(1, 1, n, L, R);
}
}seg;
struct tree
{
struct node
{
vector<node*> edg;
node *suc[2], *pre, *anc[LOG];
int isRoot, L, R, dep;
inline node() {suc[0] = suc[1] = NULL; isRoot = 1; edg.clear();}
inline int getRelation()
{
return pre == NULL ? -1 : this == pre->suc[1];
}
}nd[N + 1];
inline void addEdge(int u, int v) {nd[u].edg.push_back(nd + v); nd[v].edg.push_back(nd + u);}
int clk;
void DFS(node *u, node *pre)
{
u->dep = pre == NULL ? 1 : pre->dep + 1;
u->anc[0] = pre;
for(int i = 1; i < LOG; ++ i) if(u->anc[i - 1] != NULL) u->anc[i] = u->anc[i - 1]->anc[i - 1];
u->pre = pre; seg.modify(u->L = u->R = ++ clk, u->dep);
for(auto v : u->edg) if(v != pre) DFS(v, u), u->R = v->R;
}
inline void build()
{
clk = 0;
DFS(nd + 1, NULL);
}
inline void rotate(node *u)
{
node *pre = u->pre, *prepre = u->pre->pre;
int k = u->getRelation();
if(u->suc[k ^ 1] != NULL) u->suc[k ^ 1]->pre = pre; pre->suc[k] = u->suc[k ^ 1];
u->pre = prepre; if(! pre->isRoot) prepre->suc[pre->getRelation()] = u; //注意这里应该写的是isRoot
pre->pre = u; u->suc[k ^ 1] = pre;
if(pre->isRoot) pre->isRoot = 0, u->isRoot = 1;
}
inline void splay(node *u)
{
while(! u->isRoot)
{
if(! u->pre->isRoot) rotate(u->getRelation() == u->pre->getRelation() ? u->pre : u);
rotate(u);
}
}
inline node* getSubtreeRoot(node *u)
{
for(; u->suc[0] != NULL; u = u->suc[0]);
return u;
}
inline void modify(int _u)
{
node *u = nd + _u;
splay(u);
if(u->suc[1] != NULL)
{
u->suc[1]->isRoot = 1;
node *rt = getSubtreeRoot(u->suc[1]); seg.modify(rt->L, rt->R, 1);
u->suc[1] = NULL;
}
while(u->pre != NULL)
{
splay(u->pre);
if(u->pre->suc[1] != NULL)
{
u->pre->suc[1]->isRoot = 1;
node *rt = getSubtreeRoot(u->pre->suc[1]); seg.modify(rt->L, rt->R, 1);
}
node *rt = getSubtreeRoot(u); seg.modify(rt->L, rt->R, -1);
u->pre->suc[1] = u; u->isRoot = 0;
splay(u);
}
}
inline node* getLCA(node *u, node *v)
{
if(u->dep < v->dep) swap(u, v);
for(int i = LOG - 1; ~ i; -- i) if(u->dep - (1 << i) >= v->dep) u = u->anc[i];
if(u == v) return u;
for(int i = LOG - 1; ~ i; -- i) if(u->anc[i] != v->anc[i]) u = u->anc[i], v = v->anc[i];
return u->anc[0];
}
inline int query(int u, int v)
{
node *LCA = getLCA(nd + u, nd + v);
return seg.query(nd[u].L, nd[u].L) + seg.query(nd[v].L, nd[v].L) - 2 * seg.query(LCA->L, LCA->L) + 1;
}
inline int query(int pos)
{
return seg.query(nd[pos].L, nd[pos].R);
}
}T;
int main()
{
#ifndef ONLINE_JUDGE
freopen("BZOJ4817.in", "r", stdin);
freopen("BZOJ4817.out", "w", stdout);
#endif
using namespace Zeonfai;
n = getInt(), m = getInt();
for(int i = 1; i < n; ++ i)
{
int u = getInt(), v = getInt();
T.addEdge(u, v);
}
T.build();
for(int i = 0; i < m; ++ i)
{
int opt = getInt();
if(opt == 1)
{
int u = getInt();
T.modify(u);
}
else if(opt == 2)
{
int u = getInt(), v = getInt();
printf("%d
", T.query(u, v));
}
else if(opt == 3)
{
int u = getInt();
printf("%d
", T.query(u));
}
}
}