Bob有一棵$n$个点的有根树,其中$1$号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。
定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。
Bob可能会进行这几种操作:
1 x
把点$x$到根节点的路径上所有的点染上一种没有用过的新颜色。2 x y
求$x$到$y$的路径的权值。3 x
在以$x$为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。
Bob一共会进行$m$次操作。
$n,m leqslant 10^5$。
题解
这道题我是按着LCT的标签找的,然而看到这道题的时候满脑子都是树剖……根本看不出哪里要用LCT……个人觉得这题还是蛮巧妙的。
首先,如果我们把同一种颜色的点放到LCT的一棵Splay中去,那么可以发现,1操作恰好就是LCT的access
操作了。
对于一个点,他到根节点的权值,也就是LCT中这个点到根节点的实链的条数。记$f[i]$表示$i$号节点到根的权值。由这个权值的性质,我们不难得到,对于两个点$x$与$y$,$x$到$y$的路径上的权值为$$f[x]+f[y]-2f[lca(x,y)]+1$$
于是第二个操作也解决了。
第三个操作,我们不难想到把dfs序抽出来,在线段树上查询即可。
最后我们还有一个问题,也就是$f$数组该如何维护。其实不难想到,在access
操作把一条虚边变为实边,我们就把这条虚边对应的子树在线段树上$-1$,若将一条实边变为虚边,就把实边对应的子树在线段树上$+1$。于是问题得到了解决。
时间复杂度$O(nlog^2n)$。
代码实现上遇到了一些麻烦,这里讲一下我写的时候遇到的几个错误:
- 注意线段树初始化的值为这个点在原树上的深度+1。
- 还是原树与辅助树的问题,
access
操作时,当我们要对线段树上修改的时候,假如我们在Splay上要把实边$u$到$v$去掉,在线段树上修改的时候,并不是直接把$dfn[v]$到$dfn[v]+size[v]-1$这一段的答案$+1$,而应该找到$v$点所在的Splay中一直往左走,走到这棵Splay中深度最浅的点,再在线段树上进行修改。
代码
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 | using namespace std;typedef long long ll;const int INF=1e9;const double eps=1e-9;const int maxn=1e5+10;int to[maxn<<1],nex[maxn<<1],beg[maxn],dfn[maxn],id[maxn],f[maxn][20],dep[maxn],size[maxn];int e,dfs_time,n;inline int (){ int x=0,flag=1; char ch=getchar(); while(!isdigit(ch) && ch!='-')ch=getchar(); if(ch=='-')flag=-1,ch=getchar(); while(isdigit(ch))x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*flag;}struct Seg_T{ int Seg[maxn<<2],Tag[maxn<<2]; void Build(int h,int l,int r){ if(l==r){ Seg[h]=dep[id[l]]; return; } int mid=(l+r)>>1; Build(h<<1,l,mid),Build(h<<1|1,mid+1,r); Seg[h]=max(Seg[h<<1],Seg[h<<1|1]); } inline void Push_Down(int h){ if(Tag[h]){ Tag[h<<1]+=Tag[h],Seg[h<<1]+=Tag[h]; Tag[h<<1|1]+=Tag[h],Seg[h<<1|1]+=Tag[h]; Tag[h]=0; } } void Modify(int h,int l,int r,int L,int R,int val){ if(L<=l && r<=R){ Seg[h]+=val,Tag[h]+=val; return; } Push_Down(h); int mid=(l+r)>>1; if(L<=mid)Modify(h<<1,l,mid,L,R,val); if(R>mid)Modify(h<<1|1,mid+1,r,L,R,val); Seg[h]=max(Seg[h<<1],Seg[h<<1|1]); } int Query(int h,int l,int r,int L,int R){ if(L<=l && r<=R)return Seg[h]; Push_Down(h); int mid=(l+r)>>1,ans=0; if(L<=mid)ans=max(ans,Query(h<<1,l,mid,L,R)); if(R>mid)ans=max(ans,Query(h<<1|1,mid+1,r,L,R)); return ans; }}Seg;struct LCT{#define rel(x) (ch[fa[x]][1]==x) int ch[maxn][2],fa[maxn]; inline int isroot(int x){ return ch[fa[x]][0]!=x && ch[fa[x]][1]!=x; } inline void Rotate(int x){ int y=fa[x],t=fa[y],flag=rel(x); ch[fa[ch[x][flag^1]]=y][flag]=ch[x][flag^1]; fa[x]=t;if(!isroot(y))ch[t][rel(y)]=x; ch[fa[y]=x][flag^1]=y; } inline void Splay(int x){ for(;!isroot(x);Rotate(x)) if(!isroot(fa[x]))Rotate(rel(x)^rel(fa[x])?x:fa[x]); } inline int Getl(int x){ while(ch[x][0])x=ch[x][0]; return x; } inline void Access(int x){ for(int i=0;x;x=fa[i=x]){ Splay(x); if(ch[x][1]){ int pos=Getl(ch[x][1]); Seg.Modify(1,1,n,dfn[pos],dfn[pos]+size[pos]-1,1); } ch[x][1]=i; if(i){ int pos=Getl(i); Seg.Modify(1,1,n,dfn[pos],dfn[pos]+size[pos]-1,-1); } } }}T;inline void add(int x,int y){ to[++e]=y; nex[e]=beg[x]; beg[x]=e;}void dfs(int x,int fa){ int i; dfn[x]=++dfs_time;id[dfs_time]=x; f[x][0]=fa;T.fa[x]=fa;size[x]=1; for(i=beg[x];i;i=nex[i]){ if(to[i]==fa)continue; dep[to[i]]=dep[x]+1; dfs(to[i],x); size[x]+=size[to[i]]; }}inline int Get_LCA(int x,int y){ int i; if(dep[x]<dep[y])swap(x,y); for(i=18;i>=0;i--) if((dep[x]-dep[y]) & (1<<i)) x=f[x][i]; if(x==y)return x; for(i=18;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0];}int main(){ int i,j,q;#ifndef ONLINE_JUDGE freopen("BZOJ4817.in","r",stdin); freopen("BZOJ4817.out","w",stdout);#endif n=read();q=read(); for(i=1;i<n;i++){ int x=read(),y=read(); add(x,y);add(y,x); } dep[1]=1;dfs(1,0); for(i=1;i<=18;i++) for(j=1;j<=n;j++) f[j][i]=f[f[j][i-1]][i-1]; Seg.Build(1,1,n); while(q--){ int opt=read(); if(opt==1){ int x=read(); T.Access(x); } if(opt==2){ int x=read(),y=read(),lca=Get_LCA(x,y); int ans=Seg.Query(1,1,n,dfn[x],dfn[x])+Seg.Query(1,1,n,dfn[y],dfn[y]); printf("%dn",ans-2*Seg.Query(1,1,n,dfn[lca],dfn[lca])+1); } if(opt==3){ int x=read(); printf("%dn",Seg.Query(1,1,n,dfn[x],dfn[x]+size[x]-1)); } } return 0;} |
原文:大专栏 BZOJ4817 [SDOI2017]树点涂色