[bzoj4817] [SDOI2017]树点涂色
Description
Bob有一棵n个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。定义一条路
径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。Bob可能会进行这几种操作:
1 x:
把点x到根节点的路径上所有的点染上一种没有用过的新颜色。
2 x y:
求x到y的路径的权值。
3 x
在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。
Bob一共会进行m次操作
Input
第一行两个数n,m。
接下来n-1行,每行两个数a,b,表示a与b之间有一条边。
接下来m行,表示操作,格式见题目描述
1<=n,m<=100000
Output
每当出现2,3操作,输出一行。
如果是2操作,输出一个数表示路径的权值
如果是3操作,输出一个数表示权值的最大值
Sample Input
5 6
1 2
2 3
3 4
3 5
2 4 5
3 3
1 4
2 4 5
1 5
2 4 5
Sample Output
3
4
2
2
solution
\(LCT\)好题。
对于\(1\)操作,注意到每次都是从\(x\)到根的修改,而且每次颜色都不一样,可以类比于\(LCT\)的\(access\)。
所以开一颗\(LCT\),\(LCT\)内每一颗\(splay\)维护一种颜色,然后改颜色直接\(access\)就好了。
考虑怎么维护问的东西。
注意到要同时维护链上信息和子树信息,可以考虑拿一个\(dfs\)序建的线段树来维护每个点到根节点路径的权值。
然后每次\(access\)的时候,实边变虚边就把子树内所有权值\(+1\),反之\(-1\)。
然后对于\(3\)操作拿线段树记一下\(max\)就好了。
#include<bits/stdc++.h>
using namespace std;
void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
const int maxn = 1e5+1;
int fa[maxn],son[maxn][2],dfn[maxn],sz[maxn],head[maxn],tot,dfn_cnt,R[maxn],dep[maxn],n,m,f[maxn][20];
struct edge {int to,nxt;}e[maxn<<1];
void add(int u,int v) {e[++tot]=(edge){v,head[u]},head[u]=tot;}
void ins(int u,int v) {add(u,v),add(v,u);}
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)
struct Segment_Tree {
int tr[maxn],tag[maxn];
void update(int p) {tr[p]=max(tr[ls],tr[rs]);}
void push_tag(int p,int v) {tr[p]+=v,tag[p]+=v;}
void pushdown(int p) {
if(!tag[p]) return ;
tr[ls]+=tag[p],tr[rs]+=tag[p],tag[ls]+=tag[p],tag[rs]+=tag[p];
tag[p]=0;
}
void modify(int p,int l,int r,int x,int y,int v) {
if(x<=l&&r<=y) return tr[p]+=v,tag[p]+=v,void();
pushdown(p);
if(x<=mid) modify(ls,l,mid,x,y,v);
if(y>mid) modify(rs,mid+1,r,x,y,v);
update(p);
}
int query(int p,int l,int r,int x,int y) {
if(x<=l&&r<=y) return tr[p];
int ans=0;pushdown(p);
if(x<=mid) ans=max(ans,query(ls,l,mid,x,y));
if(y>mid) ans=max(ans,query(rs,mid+1,r,x,y));
return ans;
}
void build(int p,int l,int r) {
if(l==r) return tr[p]=dep[R[l]],void();
build(ls,l,mid),build(rs,mid+1,r);
update(p);
}
}SGT;
#undef ls
#undef rs
#undef mid
struct Input_Tree {
void dfs(int x,int F) {
fa[x]=F,sz[x]=1,dfn[x]=++dfn_cnt,R[dfn_cnt]=x,f[x][0]=F,dep[x]=dep[F]+1;
for(int i=1;i<=19;i++) f[x][i]=f[f[x][i-1]][i-1];
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=F) dfs(e[i].to,x),sz[x]+=sz[e[i].to];
}
int lca(int x,int y) {
if(dep[x]<dep[y]) swap(x,y);
for(int i=19;~i;i--) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return x;
for(int i=19;~i;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
}T;
struct Link_Cut_Tree {
int is_root(int x) {return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;}
int which(int x) {return son[fa[x]][1]==x;}
void rotate(int x) {
int F=fa[x],ff=fa[F],w=which(x);
if(!is_root(F)) son[ff][son[ff][1]==F]=x;
fa[x]=ff,fa[F]=x,fa[son[x][w^1]]=F,son[F][w]=son[x][w^1],son[x][w^1]=F;
}
void splay(int x) {
while(!is_root(x)) {
int y=fa[x],z=fa[y];
if(!is_root(y)) rotate(((son[y][1]==x)^(son[z][1]==y))?x:y);
rotate(x);
}
}
int pre(int x) {
while(son[x][0]) x=son[x][0];
return x;
}
void access(int x) {
for(int t=0;x;t=x,x=fa[x]) {
splay(x);
if(son[x][1]) {
int p=pre(son[x][1]);
SGT.modify(1,1,n,dfn[p],dfn[p]+sz[p]-1,1);
}
son[x][1]=t;
if(t) {
int p=pre(t);
SGT.modify(1,1,n,dfn[p],dfn[p]+sz[p]-1,-1);
}
}
}
}LCT;
int main() {
read(n),read(m);
for(int i=1,x,y;i<n;i++) read(x),read(y),ins(x,y);
T.dfs(1,0);SGT.build(1,1,n);
for(int op,x,y,i=1;i<=m;i++) {
read(op),read(x);
if(op==1) LCT.access(x);
else if(op==2) {
read(y);int ans=1,tmp=dfn[T.lca(x,y)];
ans+=SGT.query(1,1,n,dfn[x],dfn[x])+SGT.query(1,1,n,dfn[y],dfn[y]);
ans-=2*SGT.query(1,1,n,tmp,tmp);write(ans);
} else write(SGT.query(1,1,n,dfn[x],dfn[x]+sz[x]-1));
}
return 0;
}