BZOJ4817[Sdoi2017]树点涂色——LCT+线段树

时间:2021-02-25 04:13:34

题目描述

Bob有一棵n个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。定义一条路
径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。Bob可能会进行这几种操作:
1 x:
把点x到根节点的路径上所有的点染上一种没有用过的新颜色。
2 x y:
求x到y的路径的权值。
3 x
在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。
Bob一共会进行m次操作

输入

第一行两个数n,m。
接下来n-1行,每行两个数a,b,表示a与b之间有一条边。
接下来m行,表示操作,格式见题目描述
1<=n,m<=100000

输出

每当出现2,3操作,输出一行。
如果是2操作,输出一个数表示路径的权值
如果是3操作,输出一个数表示权值的最大值

样例输入

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

样例输出

3
4
2
2
 
我们将连续相同颜色的一条链看成一条重链,可以发现修改操作其实就是$LCT$里的$access$操作。
那么每个节点到根的权值就是这个点到根路径上的重链数。
考虑用线段树维护出每个点到根的权值,每次修改时假设路径上$x->fa$的边是轻边,$fa$的重儿子是$son$,那么就将$son$子树中所有点的答案$+1$,$x$子树中所有点答案$-1$,然后将$x$变为$fa$的重儿子。在$access$时完成子树修改操作即可。
对于查询$x$到$y$的路径权值,就是$v[x]+v[y]-2v[lca]+1$。
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int c[100010][2];
int fa[100010];
int tag[400010];
int f[100010][18];
int dep[100010];
int mx[400010];
int dfn;
int tot;
int x,y;
int n,m;
int opt;
int head[100010];
int next[200010];
int to[200010];
int q[100010];
int s[100010];
int t[100010];
void add(int x,int y)
{
next[++tot]=head[x];
head[x]=tot;
to[tot]=y;
}
void pushup(int rt)
{
mx[rt]=max(mx[rt<<1],mx[rt<<1|1]);
}
void pushdown(int rt)
{
if(tag[rt])
{
tag[rt<<1]+=tag[rt];
tag[rt<<1|1]+=tag[rt];
mx[rt<<1]+=tag[rt];
mx[rt<<1|1]+=tag[rt];
tag[rt]=0;
}
}
void build(int rt,int l,int r)
{
if(l==r)
{
mx[rt]=dep[q[l]];
return ;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
void change(int rt,int l,int r,int L,int R,int v)
{
if(L<=l&&r<=R)
{
tag[rt]+=v;
mx[rt]+=v;
return ;
}
pushdown(rt);
int mid=(l+r)>>1;
if(L<=mid)
{
change(rt<<1,l,mid,L,R,v);
}
if(R>mid)
{
change(rt<<1|1,mid+1,r,L,R,v);
}
pushup(rt);
}
int query(int rt,int l,int r,int L,int R)
{
if(L<=l&&r<=R)
{
return mx[rt];
}
pushdown(rt);
int mid=(l+r)>>1;
int res=0;
if(L<=mid)
{
res=max(res,query(rt<<1,l,mid,L,R));
}
if(R>mid)
{
res=max(res,query(rt<<1|1,mid+1,r,L,R));
}
return res;
}
int ask(int rt,int l,int r,int k)
{
if(l==r)
{
return mx[rt];
}
pushdown(rt);
int mid=(l+r)>>1;
if(k<=mid)
{
return ask(rt<<1,l,mid,k);
}
else
{
return ask(rt<<1|1,mid+1,r,k);
}
}
bool is_root(int rt)
{
return rt!=c[fa[rt]][0]&&rt!=c[fa[rt]][1];
}
bool get(int rt)
{
return rt==c[fa[rt]][1];
}
void rotate(int rt)
{
int x=fa[rt];
int y=fa[x];
int k=get(rt);
if(!is_root(x))
{
c[y][get(x)]=rt;
}
c[x][k]=c[rt][k^1];
fa[c[rt][k^1]]=x;
c[rt][k^1]=x;
fa[x]=rt;
fa[rt]=y;
}
void splay(int rt)
{
for(int x;!is_root(rt);rotate(rt))
{
if(!is_root(x=fa[rt]))
{
rotate(get(x)==get(rt)?x:rt);
}
}
}
void link(int x,int y)
{
fa[x]=y;
}
int find(int rt)
{
while(c[rt][0])
{
rt=c[rt][0];
}
return rt;
}
void access(int rt)
{
for(int x=0;rt;x=rt,rt=fa[rt])
{
splay(rt);
if(x)
{
int son=find(x);
change(1,1,n,s[son],t[son],-1);
}
if(c[rt][1])
{
int son=find(c[rt][1]);
change(1,1,n,s[son],t[son],1);
}
c[rt][1]=x;
}
}
void dfs(int x)
{
s[x]=++dfn;
q[dfn]=x;
for(int i=1;i<=17;i++)
{
f[x][i]=f[f[x][i-1]][i-1];
}
for(int i=head[x];i;i=next[i])
{
if(to[i]!=f[x][0])
{
f[to[i]][0]=x;
link(to[i],x);
dep[to[i]]=dep[x]+1;
dfs(to[i]);
}
}
t[x]=dfn;
}
int lca(int x,int y)
{
if(dep[x]<dep[y])
{
swap(x,y);
}
int d=dep[x]-dep[y];
for(int i=0;i<=17;i++)
{
if(d&(1<<i))
{
x=f[x][i];
}
}
if(x==y)
{
return x;
}
for(int i=17;i>=0;i--)
{
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dep[1]=1;
dfs(1);
build(1,1,n);
while(m--)
{
scanf("%d",&opt);
if(opt==1)
{
scanf("%d",&x);
access(x);
}
else if(opt==2)
{
scanf("%d%d",&x,&y);
int anc=lca(x,y);
int ans=ask(1,1,n,s[x])+ask(1,1,n,s[y])-2*ask(1,1,n,s[anc])+1;
printf("%d\n",ans);
}
else
{
scanf("%d",&x);
printf("%d\n",query(1,1,n,s[x],t[x]));
}
}
}