Description
有一棵树,点带颜色,初始颜色各不相同
定义路径价值为路径上面不同颜色的个数
请写出一种数据结构,支持以下几种操作:
(1.) 更新根到某点的链上的颜色为一种新的
(2.) 求一个点子树到中到根路径价值最大的点(求最大价值)
(3.) 求两点之间的路径价值
Solution
首先考虑的是每个颜色的贡献
看到(1) 操作,想到了 (Link-Cut Tree)
但是看到 (2,3) 操作,想到了树剖
然后发现树剖中可以拿来解决这题的其实是线段树维护 (dfn) 序
(2) 操作变成了区间最值,(3) 操作变成了树上差分,再做一下 (lca)
然后还是 (lct)
(1) 操作的本质变成了 (access)
就是像 (HH) 的项链那样子,把原来的那个颜色删除,在新的地方添加一个新的颜色
然后就变成了实现代码题(真的很难实现)
细节总览:
(1.) 这里的 (lct) 没有维护信息,只是拿来整路径的
(2.) 线段树的实现比较恶心,修改的时候不能 (l<=l(p)&&r(p)<=r,)得两边都满足的时候才能改
(3.) (access) 的时候先对子树上面的 (val) 减去 (1),之后跳到另一棵 (splay) 的时候给那棵的点权再加上 (1)
(其实就是依据实际情况对两种数据结构进行了调整)
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace yspm{
inline int read()
{
int res=0,f=1; char k;
while(!isdigit(k=getchar())) if(k=='-') f=-1;
while(isdigit(k)) res=res*10+k-'0',k=getchar();
return res*f;
}
const int N=1e5+10;
int fa[N],ls[N],rs[N],l[N],r[N],dfn[N],tot,anc[N][20];
int head[N],cnt,dep[N],n,m;
struct node{
int to,nxt;
}e[N<<1];
inline void add(int u,int v)
{
e[++cnt].nxt=head[u]; e[cnt].to=v;
return head[u]=cnt,void();
}
inline void dfs(int x,int fat)
{
dep[x]=dep[fat]+1; dfn[++tot]=x; l[x]=tot; anc[x][0]=fat; fa[x]=fat;
for(int i=1;(1<<i)<=dep[x];++i) anc[x][i]=anc[anc[x][i-1]][i-1];
for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fat) dfs(e[i].to,x);
r[x]=tot;
return ;
}
inline int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=19;i>=0;--i) if(dep[anc[x][i]]>=dep[y]) x=anc[x][i];
if(x==y) return x;
for(int i=19;i>=0;--i) if(anc[x][i]!=anc[y][i]) x=anc[x][i],y=anc[y][i];
return anc[x][0];
}
struct tree{
int maxx,fl,l,r;
#define maxx(p) t[p].maxx
#define l(p) t[p].l
#define r(p) t[p].r
#define fl(p) t[p].fl
}t[N<<2];
inline void push_up(int x){return maxx(x)=max(maxx(x<<1),maxx(x<<1|1)),void();}
inline void build(int p,int st,int ed)
{
l(p)=st; r(p)=ed;
if(st==ed) return maxx(p)=dep[dfn[st]],void();
int mid=(st+ed)>>1; build(p<<1,st,mid); build(p<<1|1,mid+1,ed);
return push_up(p);
}
inline void push_down(int p)
{
if(fl(p))
{
fl(p<<1)+=fl(p); maxx(p<<1)+=fl(p);
maxx(p<<1|1)+=fl(p); fl(p<<1|1)+=fl(p);
} return fl(p)=0,void();
}
inline void update(int p,int st,int ed,int val)
{
if(st==l(p)&&r(p)==ed) return fl(p)+=val,maxx(p)+=val,void();
int mid=(l(p)+r(p))>>1; push_down(p);
if(ed<=mid) update(p<<1,st,ed,val);
else if(st>mid) update(p<<1|1,st,ed,val);
else update(p<<1,st,mid,val),update(p<<1|1,mid+1,ed,val);
return push_up(p);
}
inline int query(int p,int st,int ed)
{
if(st==l(p)&&r(p)==ed) return maxx(p);
int mid=(l(p)+r(p))>>1; push_down(p);
if(ed<=mid) return query(p<<1,st,ed);
if(st>mid) return query(p<<1|1,st,ed);
return max(query(p<<1|1,mid+1,ed),query(p<<1,st,mid));
}
inline bool isroot(int x){return ls[fa[x]]!=x&&rs[fa[x]]!=x;}
inline void rotate(int x)
{
int y=fa[x],z=fa[y];
if(!isroot(y)) if(ls[z]==y) ls[z]=x; else rs[z]=x;
if(ls[y]==x) ls[y]=rs[x],fa[rs[x]]=y,rs[x]=y;
else rs[y]=ls[x],fa[ls[x]]=y,ls[x]=y;
fa[x]=z; fa[y]=x;
return ;
}
inline void splay(int x)
{
while(!isroot(x))
{
int y=fa[x],z=fa[y];
if(!isroot(y)) rotate((ls[y]==x)^(ls[z]==y)?x:y);
rotate(x);
} return ;
}
inline int findroot(int x){while(ls[x]) x=ls[x]; return x;}
inline void access(int x)
{
//rs[x] 在这里是深度大的点,而ls[x] 是深度小的
for(int y=0;x;x=fa[y=x])
{
splay(x);
if(rs[x])
{
int t=rs[x]; t=findroot(t);
update(1,l[t],r[t],1);//把新的颜色添加上去
} rs[x]=y; //lct操作
if(rs[x])
{
int t=rs[x]; t=findroot(t);
update(1,l[t],r[t],-1);//删掉原来颜色的贡献
}
} return ;
}
signed main()
{
n=read(); m=read();
for(int i=1,u,v;i<n;++i) u=read(),v=read(),add(u,v),add(v,u);
dfs(1,0); build(1,1,n);
while(m--)
{
int opt=read();
if(opt==1) access(read());
if(opt==2)
{
int x=read(),y=read(),lc=lca(x,y);
x=l[x],y=l[y],lc=l[lc];
printf("%lld
",query(1,x,x)+query(1,y,y)-2*query(1,lc,lc)+1);
}
if(opt==3)
{
int x=read();
printf("%lld
",query(1,l[x],r[x]));
}
}
return 0;
}
}
signed main(){return yspm::main();}