Luogu P3703 [SDOI2017]树点涂色
用LCT中每一个Splay维护颜色相同的点集,则从一个点到根节点的轻边的条数就是这个点的到根的权值。至于路径查询的搞个差分就好,用树剖实现。
至于为什么可以直接这样查,是因为LCT里面涉及子树的权值变化只有access函数。在splay中的子树的切换是属于实链中的变化,对权值没有影响。
所以这题其实就是个裸树剖+LCT
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define R register
#define LL long long
const int inf = 0x3f3f3f3f;
const int MAXN = 1e5 + 10;
inline int read() {
char a = getchar(); int x = 0,f = 1;
for(; a > '9' || a < '0'; a = getchar()) if(a == '-') f = -1;
for(; a >= '0' && a <= '9' ;a = getchar()) x = x * 10 + a - '0';
return x * f;
}
namespace Data {
class SegTree {
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
private:
int tg[MAXN<<2],mx[MAXN<<2];
inline void update(int x) { mx[x]=max(mx[ls(x)], mx[rs(x)]); }
inline void get(int x,int k) { tg[x]+=k; mx[x]+=k; }
inline void pushdown(int x) {
if(tg[x]) {
if(ls(x)) get(ls(x),tg[x]);
if(rs(x)) get(rs(x),tg[x]);
tg[x]=0;
}
}
public:
inline void add(int x,int l,int r,int Le,int Ri,int k) {
if(Le<=l&&r<=Ri) { get(x,k); return ;}
pushdown(x);
int mid=l+r;mid>>=1;
if(Le<=mid) add(ls(x),l,mid,Le,Ri,k);
if(Ri>mid) add(rs(x),mid+1,r,Le,Ri,k);
update(x);
}
inline int ask(int x,int l,int r,int Le,int Ri) {
if(Le<=l&&r<=Ri) return mx[x];
pushdown(x);
int mid=l+r;mid>>=1;
int ans=-inf;
if(Le<=mid) ans=max(ans,ask(ls(x),l,mid,Le,Ri));
if(Ri>mid) ans=max(ans,ask(rs(x),mid+1,r,Le,Ri));
update(x);
return ans;
}
#undef ls
#undef rs
};
}
using Data::SegTree;
namespace Graph {
struct Edge { int to,next; } e[MAXN<<1];
int head[MAXN],cnt;
inline void add(int x,int y) { e[++cnt]={y,head[x]}; head[x]=cnt; }
int dep[MAXN],fa[MAXN],siz[MAXN],son[MAXN],top[MAXN],dfn[MAXN],tot;
inline void dfs1(int x,int fx) {
dep[x]=dep[fx]+1; fa[x]=fx; siz[x]=1; dfn[x]=++tot;
for(R int i=head[x];i;i=e[i].next) {
int y=e[i].to;
if(y==fx) continue;
dfs1(y,x);
siz[x]+=siz[y];
if(siz[y]>siz[son[x]]) son[x]=y;
}
}
inline void dfs2(int x,int topx) {
top[x]=topx;
if(son[x]) dfs2(son[x],topx);
for(R int i=head[x];i;i=e[i].next) {
int y=e[i].to;
if(y==fa[x]||y==son[x]) continue;
dfs2(y,y);
}
}
inline int lca(int x,int y) {
while(top[x]!=top[y])
if(dep[top[x]]>dep[top[y]])
x=fa[top[x]];
else
y=fa[top[y]];
return dep[x]<dep[y]?x:y;
}
inline void build() { dfs1(1,0); dfs2(1,1); }
}
using Graph::dfn;
using Graph::lca;
using Graph::build;
using Graph::add;
using Graph::siz;
using Graph::dep;
using Graph::head;
using Graph::e;
int n,m;
SegTree ST;
namespace Link_Cut_Tree {
class LCT {
#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
private:
int fa[MAXN],ch[MAXN][2];
public:
inline int nroot(int x) { return rs(fa[x])==x||ls(fa[x])==x; }
inline int get(int x) { return rs(fa[x])==x; }
inline void rotate(int x) {
int y=fa[x],z=fa[y],k=get(x),w=ch[x][!k];
if(nroot(y)) ch[z][get(y)]=x; ch[x][!k]=y; ch[y][k]=w;
if(w) fa[w]=y; fa[y]=x; fa[x]=z;
}
inline void splay(int x) {
while(nroot(x)) {
if(nroot(fa[x]))
rotate(get(x)^get(fa[x])?x:fa[x]);
rotate(x);
}
}
inline int findrt(int x) {
while(ls(x)) x=ls(x); return x;
}
inline void access(int x) {
for(R int y=0;x;x=fa[y=x]) {
splay(x);
if(rs(x)) {
int rt=findrt(rs(x));
ST.add(1,1,n,dfn[rt],dfn[rt]+siz[rt]-1,1);
}
if(y) {
int rt=findrt(y);
ST.add(1,1,n,dfn[rt],dfn[rt]+siz[rt]-1,-1);
}
rs(x)=y;
}
}
inline void build(int x,int fx) {
fa[x]=fx;
for(R int i=head[x];i;i=e[i].next) {
int y=e[i].to;
if(y==fx) continue;
build(y,x);
}
}
#undef ls
#undef rs
};
}
using Link_Cut_Tree::LCT;
LCT T;
int main() {
n=read(); m=read();
for(R int i=1;i<n;i++) {
int x=read(),y=read();
add(x,y); add(y,x);
}
build();T.build(1,0);
for(R int i=1;i<=n;i++) ST.add(1,1,n,dfn[i],dfn[i],dep[i]);
while(m--) {
int op=read(),x=read(),y;
if(op==2) y=read();
if(op==1) T.access(x);
if(op==2) {
int Lca=lca(x,y);
int s1=ST.ask(1,1,n,dfn[x],dfn[x]);
int s2=ST.ask(1,1,n,dfn[y],dfn[y]);
int s3=ST.ask(1,1,n,dfn[Lca],dfn[Lca]);
printf("%d\n",s1+s2-s3*2+1);
}
if(op==3) {
int s=ST.ask(1,1,n,dfn[x],dfn[x]+siz[x]-1);
printf("%d\n",s);
}
}
return 0;
}