bzoj4817 [Sdoi2017]树点涂色

Posted oyjason

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj4817 [Sdoi2017]树点涂色相关的知识,希望对你有一定的参考价值。

 

4817: [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操作,输出一个数表示权值的最大值
 
 
题目简述
定义树根为1
定义路径权值:连续颜色段的数量
维护一棵树,支持如下三种操作:
1、将x点到根路径上每一个点都修改成新的颜色
2、查询某条路径权值
3、查询某颗子树内点到1路径最大权值。
 
 
题解
一道非常典型的数据结构之间的相互应用的题,利用$LCT$维护路径,利用线段树维护点权最值。
 
 
我们还是先从树剖想起,于是就发现树剖套线段树能完美的解决前两种操作,但对第三种操作却束手无策。树链剖分的限制是,在任何时刻求单个点到跟的路径权值都需要$O(log(n))$的复杂度,并且无法区间求最值,所以树剖这条路看起来并不是这么可行(但据我们机房某julao说可以直接用树剖套两个线段树搞定)。
 
为了方便,我们设一个点的点权为其到根节点的路径权值。
 
我们换个思路,不难发现,对于第二种操作,假设给定了路径两个端点为$(u,v)$,设$lca(u,v)=k$,设$D(x)$为$x$的点权,则$(u,v)$路径权值为和$D(u)+D(y)-2D(k)+1$,就把我第二种操作转化为求某个点的点权了。所欲对于后两个操作,我们现在只需要维护区间点权最值即可。
 
再来关注修改,我们发现,修改与$LCT$中的$access$操作十分相似,我们考虑修改对每个点的答案的影响,如下图:
技术分享图片

红色部分是我们要修改的,对于每一个和红色有重叠的颜色段(即$access$操作中每一条原来的重链),重叠部分的染色对于原颜色段的除去与要修改的重叠以外部分(若不存在则不考虑)所在的整颗的子树的答案有+1的贡献(即图中各个颜色段与红色不重叠的部分);对于除去顶部的颜色段以外的所有颜色段的顶端的子树有-1的贡献。

对应的$LCT$中就恰好是,$access$时:

对于断开某条重链,就把下方的断点所在子树答案+1;

对于连接一条链,就把下方连接点所在子树答案-1;

用线段树维护即可,复杂度约为$O(ncdot log^2(n))$。

注意初始时每个点颜色不同,点权为其深度。

 

AC代码如下:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define mid (l+r>>1)
#define M 100010
#define ls c[x][0]
#define rs c[x][1]
using namespace std;
int read(){
	int nm=0,fh=1;char cw=getchar();
	for(;!isdigit(cw);cw=getchar()) if(cw==‘-‘) fh=-fh;
	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-‘0‘);
	return nm*fh;
}
void write(int x){
	if(x>=10) write(x/10);
	putchar(x%10+‘0‘);
}
int n,m,fs[M],tmp,to[M<<1],nt[M<<1],st[M<<2][20];
int dfn[M],tk[M],cnt,dep[M],sz[M],fst[M],tot,fa[M];
int p[M<<2],tg[M<<2],lg[M<<2],sq[30];
int u,v,T,c[M][2],tpe;

void link(int x,int y){nt[tmp]=fs[x],fs[x]=tmp,to[tmp++]=y;}
void dfs(int x){
	dfn[x]=++cnt,st[++tot][0]=dfn[x];
	sz[x]=1,fst[x]=tot,tk[cnt]=x;
	for(int i=fs[x];i!=-1;i=nt[i]){
		if(dep[to[i]]>0) continue;
		dep[to[i]]=dep[x]+1,dfs(to[i]),fa[to[i]]=x;
		st[++tot][0]=dfn[x],sz[x]+=sz[to[i]];
	}
}
void init(){
	lg[0]=-1,sq[0]=1;
	for(int i=1;i<=tot;i++) lg[i]=lg[i>>1]+1;
	for(int i=1;i<=20;i++) sq[i]=(sq[i-1]<<1);
	for(int j=1;j<19;j++){
		for(int i=1;i+sq[j]-1<=tot;i++){
			st[i][j]=min(st[i][j-1],st[i+sq[j-1]][j-1]);
		}
	}
}
int lca(int x,int y){
	int t1=fst[x],t2=fst[y],len;
	if(t1>t2) swap(t1,t2);
	len=t2-t1+1;
	return tk[min(st[t1][lg[len]],st[t2-sq[lg[len]]+1][lg[len]])];
}

void pushdown(int x){
	tg[x<<1]+=tg[x],p[x<<1]+=tg[x];
	tg[x<<1|1]+=tg[x],p[x<<1|1]+=tg[x];
	tg[x]=0; return;
}
void pushup(int x){p[x]=max(p[x<<1],p[x<<1|1]);}
void build(int x,int l,int r){
	if(l==r){p[x]=dep[tk[l]];return;}
	build(x<<1,l,mid),build(x<<1|1,mid+1,r),pushup(x);
}
void add(int x,int l,int r,int L,int R,int dt){
	if(r<L||R<l) return;
	if(L<=l&&r<=R){tg[x]+=dt,p[x]+=dt;return;}
	pushdown(x),add(x<<1,l,mid,L,R,dt);
	add(x<<1|1,mid+1,r,L,R,dt),pushup(x);
}
int query(int x,int l,int r,int L,int R){
	if(r<L||R<l) return 0;
	if(L<=l&&r<=R) return p[x];
	pushdown(x); int now=query(x<<1,l,mid,L,R);
	now=max(query(x<<1|1,mid+1,r,L,R),now);
	pushup(x); return now;
}

bool isroot(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
void rotate(int x){
	int tp=fa[x],dtp=fa[fa[x]],ds,ms;
	if(!isroot(tp)){
		if(c[dtp][0]==tp) c[dtp][0]=x;
		else c[dtp][1]=x;
    }
    if(c[tp][0]==x) ms=0,ds=1;
    else ms=1,ds=0;
    fa[x]=dtp,fa[tp]=x,fa[c[x][ds]]=tp;
    c[tp][ms]=c[x][ds],c[x][ds]=tp;
}
void splay(int x){
	while(!isroot(x)){
		int tp=fa[x];
		if(isroot(tp)) return rotate(x);
		if(c[c[fa[tp]][0]][0]==x) rotate(tp);
		else if(c[c[fa[tp]][1]][1]==x) rotate(tp);
		else rotate(x);
	}
}
void access(int x){
	int now;
	for(int last=0;x>0;rs=last,last=x,x=fa[x]){
		splay(x);
		if(rs>0){
			for(now=rs;c[now][0]>0;now=c[now][0]);
			add(1,1,n,dfn[now],dfn[now]+sz[now]-1,1);
		}
		if(last>0){
			for(now=last;c[now][0]>0;now=c[now][0]);
			add(1,1,n,dfn[now],dfn[now]+sz[now]-1,-1);
		}
	}
}
int main(){
	n=read(),T=read(),memset(fs,-1,sizeof(fs));
	for(int i=1;i<n;i++) u=read(),v=read(),link(u,v),link(v,u);
	dep[1]=1,dfs(1),init(),build(1,1,n);
	while(T--){
		tpe=read(),u=read();
		if(tpe==3) write(query(1,1,n,dfn[u],dfn[u]+sz[u]-1)),putchar(‘
‘);
		else if(tpe==1) access(u);
		else{
			v=read(),m=lca(u,v);
			u=query(1,1,n,dfn[u],dfn[u]);
			v=query(1,1,n,dfn[v],dfn[v]);
			m=query(1,1,n,dfn[m],dfn[m]);
			write(u+v-m-m+1),putchar(‘
‘);
		}
	}
	return 0;
}

以上是关于bzoj4817 [Sdoi2017]树点涂色的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 4817: [Sdoi2017]树点涂色

[Bzoj4817] [Sdoi2017]树点涂色 (LCT神题)

bzoj4817: [Sdoi2017]树点涂色

BZOJ4817 [Sdoi2017]树点涂色

bzoj4817 [Sdoi2017]树点涂色

[BZOJ4817][SDOI2017]树点涂色(LCT+DFS序线段树)