「SDOI2017」树点涂色(LCT+线段树)

可以发现更新操作就是$\text{LCT}$的$\text{Access}$操作,这个操作复杂度是$O(n\log n)$的

因此,考虑对于每次的$\text{Access}$操作,维护每个点到根的路径上不同的权值个数

每次$\text{Access}$操作只设计到合并两个链/断开一条链两种操作,可以通过线段树维护子树修改

那么修改的复杂度就是$O(n\log^2 n)$

对于二操作,自己模拟一下就知道,就是两个点的答案-2$\cdot \text{LCA}$答案+1

实现上有一些细节,就是更新的子树根节点是需要查询得到的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
const int N=1e5+10;

int n,m;
vector <int> G[N];
int L[N],R[N],dfn;
int son[N][2],fa[N],tfa[N][18],dep[N],id[N];
int s[N<<2],t[N<<2],mi[N];

void Upd(int p,int l,int r,int ql,int qr,int x) {
if(ql<=l && r<=qr) {
t[p]+=x;
return;
}
int mid=(l+r)>>1;
if(ql<=mid) Upd(p<<1,l,mid,ql,qr,x);
if(qr>mid) Upd(p<<1|1,mid+1,r,ql,qr,x);
s[p]=max(t[p<<1]+s[p<<1],t[p<<1|1]+s[p<<1|1]);
}
int Que(int p,int l,int r,int ql,int qr) {
if(ql<=l && r<=qr) return s[p]+t[p];
int mid=(l+r)>>1,res=-1e9;
if(ql<=mid) cmax(res,Que(p<<1,l,mid,ql,qr));
if(qr>mid) cmax(res,Que(p<<1|1,mid+1,r,ql,qr));
return res+t[p];
}
int Que(int p,int l,int r,int x) {
int res=0;
while(1) {
if(l==r) return res+s[p]+t[p];
int mid=(l+r)>>1;
res+=t[p];
if(x<=mid) p<<=1,r=mid;
else p=p<<1|1,l=mid+1;
}
}
void Build(int p,int l,int r) {
if(l==r) { s[p]=dep[id[l]]+1; return; }
int mid=(l+r)>>1;
Build(p<<1,l,mid),Build(p<<1|1,mid+1,r);
s[p]=max(s[p<<1],s[p<<1|1]);
}

int dir(int x){ return son[fa[x]][1]==x; }
int isroot(int x){ return !fa[x] || (son[fa[x]][0]!=x && son[fa[x]][1]!=x); }
void Up(int p) { mi[p]=son[p][0]?mi[son[p][0]]:p; }
void rotate(int u) {
int f=fa[u],ff=fa[f],d=dir(u);
fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
son[u][!d]=f,fa[f]=u; Up(f),Up(u);
}
void Splay(int x){ for(;!isroot(x);rotate(x)) if(!isroot(fa[x])) rotate((dir(x)^dir(fa[x]))?x:fa[x]); }
void Access(int x) {
for(int t=0,y;x;t=x,x=fa[x]) {
Splay(x),y=son[x][1];
if(y) Upd(1,1,n,L[mi[y]],R[mi[y]],1);
if(t) Upd(1,1,n,L[mi[t]],R[mi[t]],-1);
son[x][1]=t,Up(x);
}
} // LCT模板

void dfs(int u,int f) {
mi[u]=u,fa[u]=tfa[u][0]=f,id[L[u]=++dfn]=u;
for(int i=1;(1<<i)<=dep[u];++i) tfa[u][i]=tfa[tfa[u][i-1]][i-1];
for(int v:G[u]) if(v!=f) dep[v]=dep[u]+1,dfs(v,u);
R[u]=dfn;
}
int LCA(int x,int y) {
if(dep[x]<dep[y]) swap(x,y);
for(int i=0,del=dep[x]-dep[y];(1<<i)<=del;++i) if(del&(1<<i)) x=tfa[x][i];
if(x==y) return x;
drep(i,17,0) if(tfa[x][i]!=tfa[y][i]) x=tfa[x][i],y=tfa[y][i];
return tfa[x][0];
}

int main(){
n=rd(),m=rd();
rep(i,2,n) {
int u=rd(),v=rd();
G[u].pb(v),G[v].pb(u);
}
dfs(1,0),Build(1,1,n);
rep(i,1,m) {
int opt=rd();
if(opt==1) Access(rd());
else if(opt==2) {
int x=rd(),y=rd(),z=LCA(x,y);
printf("%d\n",Que(1,1,n,L[x])+Que(1,1,n,L[y])-2*Que(1,1,n,L[z])+1);
} else {
int x=rd();
printf("%d\n",Que(1,1,n,L[x],R[x]));
}
}
}