#轻重链剖分,线段树#洛谷 3703 [SDOI2017]树点涂色
分析
可以发现相同的颜色一定组成一个连通块。
那么操作2就相当于 \(f_x+f_y-2*f_{lca}+1\)
操作3就相当于询问 \(f\) 的最大值。
最关键的就是操作1,考虑往上跳,在同颜色与同重链之间选深度较大的一个。
那么就是要把重链上的这一段染成新的颜色,在线段树上维护颜色以及f值。
现在的链顶的子树所有的 \(f\) 都要减1,并且如果之前子树的点操作过那么就要撤销回来。
然后如果当前点不是原颜色的链底,那么当前点的那一子节点的子树就要整体加一。
颜色的链顶和链底可以在修改的时候动态维护,这样时间复杂度就是两个 \(\log\) 的
代码
#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
using namespace std;
const int N=100011; struct node{int y,next;}e[N<<1]; vector<int>K[N];
int as[N],dep[N],siz[N],tot,ext,n,m,et=1,fat[N],big[N],Top[N],dfn[N],rfn[N],nfd[N];
int lazy[N<<2],w[N<<2],col[N<<2],ct[N<<1],cb[N<<1];
int iut(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
void dfs1(int x,int fa){
dep[x]=dep[fa]+1,siz[x]=1,fat[x]=fa;
for (int i=as[x],SIZ=-1;i;i=e[i].next)
if (e[i].y!=fa){
dfs1(e[i].y,x),siz[x]+=siz[e[i].y];
if (SIZ<siz[e[i].y]) big[x]=e[i].y,SIZ=siz[e[i].y];
}
}
void dfs2(int x,int linp){
dfn[x]=++tot,nfd[tot]=x,Top[x]=linp;
if (!big[x]) return;
dfs2(big[x],linp),K[x].push_back(dfn[big[x]]);
for (int i=as[x];i;i=e[i].next)
if (e[i].y!=fat[x]&&e[i].y!=big[x])
dfs2(e[i].y,e[i].y),K[x].push_back(dfn[e[i].y]);
}
int max(int a,int b){return a>b?a:b;}
void build(int k,int l,int r){
if (l==r){
w[k]=dep[nfd[l]],col[k]=nfd[l];
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
w[k]=max(w[k<<1],w[k<<1|1]);
}
int lca(int x,int y){
for (;Top[x]!=Top[y];x=fat[Top[x]])
if (dep[Top[x]]<dep[Top[y]]) x^=y,y^=x,x^=y;
return dep[x]<dep[y]?x:y;
}
void ptag(int k,int z){w[k]+=z,lazy[k]+=z;}
void update(int k,int l,int r,int x,int y,int z){
if (l==x&&r==y) {ptag(k,z); return;}
int mid=(l+r)>>1;
if (lazy[k]){
ptag(k<<1,lazy[k]);
ptag(k<<1|1,lazy[k]);
lazy[k]=0;
}
if (y<=mid) update(k<<1,l,mid,x,y,z);
else if (x>mid) update(k<<1|1,mid+1,r,x,y,z);
else update(k<<1,l,mid,x,mid,z),update(k<<1|1,mid+1,r,mid+1,y,z);
w[k]=max(w[k<<1],w[k<<1|1]);
}
void upd(int k,int l,int r,int x,int y,int z){
if (l==x&&r==y) {col[k]=z; return;}
int mid=(l+r)>>1;
if (y<=mid) upd(k<<1,l,mid,x,y,z);
else if (x>mid) upd(k<<1|1,mid+1,r,x,y,z);
else upd(k<<1,l,mid,x,mid,z),upd(k<<1|1,mid+1,r,mid+1,y,z);
}
int query_col(int k,int l,int r,int x){
if (l==r) return col[k];
int mid=(l+r)>>1;
if (x<=mid) return max(col[k],query_col(k<<1,l,mid,x));
else return max(col[k],query_col(k<<1|1,mid+1,r,x));
}
int query(int k,int l,int r,int x,int y){
if (l==x&&r==y) return w[k];
int mid=(l+r)>>1;
if (lazy[k]){
ptag(k<<1,lazy[k]);
ptag(k<<1|1,lazy[k]);
lazy[k]=0;
}
if (y<=mid) return query(k<<1,l,mid,x,y);
else if (x>mid) return query(k<<1|1,mid+1,r,x,y);
else return max(query(k<<1,l,mid,x,mid),query(k<<1|1,mid+1,r,mid+1,y));
}
void Update(int x){
ct[++ext]=1,cb[ext]=x;
for (int lst=0,Ct=0;x;x=fat[lst]){
int col=query_col(1,1,n,dfn[x]);
int w=query(1,1,n,dfn[x],dfn[x]);
if (x!=cb[col]){
int now=nfd[*(--upper_bound(K[x].begin(),K[x].end(),dfn[cb[col]]))];
if (now!=lst) update(1,1,n,dfn[now],rfn[now],1),Ct=now;
}
if (dep[ct[col]]<dep[Top[x]]){
update(1,1,n,dfn[Top[x]],rfn[Top[x]],1-w);
upd(1,1,n,dfn[Top[x]],dfn[x],ext);
if (lst) update(1,1,n,dfn[lst],rfn[lst],w-1);
lst=Top[x];
}else{
update(1,1,n,dfn[ct[col]],rfn[ct[col]],1-w);
upd(1,1,n,dfn[ct[col]],dfn[x],ext);
if (lst) update(1,1,n,dfn[lst],rfn[lst],w-1);
lst=ct[col],ct[col]=Ct;
}
}
}
int main(){
ext=n=iut(),m=iut();
for (int i=1;i<n;++i){
int x=iut(),y=iut();
e[++et]=(node){y,as[x]},as[x]=et;
e[++et]=(node){x,as[y]},as[y]=et;
}
for (int i=1;i<=n;++i) ct[i]=cb[i]=i;
dfs1(1,0),dfs2(1,1),build(1,1,n);
for (int i=1;i<=n;++i) rfn[i]=dfn[i]+siz[i]-1;
for (int i=1;i<=m;++i){
int opt=iut(),x=iut();
switch (opt){
case 1:{
Update(x);
break;
}
case 2:{
int y=iut(),Lca=lca(x,y),t0,t1,t2;
t0=query(1,1,n,dfn[x],dfn[x]);
t1=query(1,1,n,dfn[y],dfn[y]);
t2=query(1,1,n,dfn[Lca],dfn[Lca]);
print(t0+t1-t2*2+1),putchar(10);
break;
}
case 3:{
print(query(1,1,n,dfn[x],rfn[x])),putchar(10);
break;
}
}
}
return 0;
}