SDOI2017 树点涂色

传送门

涂色的操作和 a c c e s s access access很像啊,如何用 L C T LCT LCT维护这个东西呢。

由于每次覆盖的颜色都不同,且是从当前到结点覆盖到根节点。
那么如果把颜色相同的一段维护在一条重链上,
一个点到根要经过多少虚边也就包含多少颜色。

所以用 L C T LCT LCT模拟覆盖的过程,用线段树维护即可。
虚变实就是子树减,实变虚就是子树加。
询问1就是 v a l [ x ] + v a l [ y ] − 2 ∗ v a l [ l c a ( x , y ) ] + 1 val[x]+val[y]-2*val[lca(x,y)]+1 val[x]+val[y]2val[lca(x,y)]+1
询问3就是区间最大值。

写的时候要想好 L C T LCT LCT在原树中的对应点。
以及初始的时候是一个点一个 s p l a y splay splay

#include<bits/stdc++.h>
#define re register
#define cs const

using std::swap;

cs int N=1e5+10;
int Head[N],Next[N<<1],V[N<<1],cnt=0;
int siz[N],dep[N],top[N],fa[N],son[N],L[N],R[N],t[N],tot=0;

namespace IO{
	cs int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
	template<typename T>
	inline T get(){
		char ch=gc();T x=0;
		while(!isdigit(ch)) ch=gc();
		while(isdigit(ch)) x=((x+(x<<2))<<1)+(ch^48),ch=gc();
		return x;
	}
	inline int gi(){return get<int>();}
}
using IO::gi;

int n,m,u,v,g,op;

namespace SGT{
	#define lc (root<<1)
	#define rc (root<<1|1)
	#define mid (T[root].l+T[root].r>>1)
	struct node{int l,r,tag,mx;}T[N<<2];
	inline void pushup(int root){T[root].mx=std::max(T[lc].mx,T[rc].mx);}
	inline void pushnow(int root,int val){T[root].mx+=val,T[root].tag+=val;}
	inline void pushdown(int root){
		if(T[root].tag){
			pushnow(lc,T[root].tag),
			pushnow(rc,T[root].tag),
			T[root].tag=0;
		}
	}
	inline void build(int root,int l,int r){
		T[root].l=l,T[root].r=r;
		if(l==r){T[root].mx=dep[t[l]],T[root].tag=0;return;}
		build(lc,l,mid),build(rc,mid+1,r),pushup(root);
	}
	inline void update(int root,int l,int r,int val){
		if(l<=T[root].l&&T[root].r<=r) return pushnow(root,val);
		pushdown(root);
		if(l> mid) return update(rc,l,r,val),pushup(root);
		if(r<=mid) return update(lc,l,r,val),pushup(root);
		update(lc,l,mid,val),update(rc,mid+1,r,val),pushup(root);
	}
	inline int query_max(int root,int l,int r){
		if(l<=T[root].l&&T[root].r<=r) return T[root].mx;
		pushdown(root);
		if(l> mid) return query_max(rc,l,r);
		if(r<=mid) return query_max(lc,l,r);
		return std::max(query_max(lc,l,mid),query_max(rc,mid+1,r));
	}
	inline int val(int root,int pos){
		if(T[root].l==T[root].r) return T[root].mx;
		pushdown(root);
		return pos<=mid?val(lc,pos):val(rc,pos);
	}
	#undef lc
	#undef rc
	#undef mid
}
using SGT::val;

namespace LCT{
	int son[N][2],fa[N];
	inline int get(int x){return x==son[fa[x]][1];}
	inline int isroot(int x){return (!fa[x])||(x!=son[fa[x]][0]&&x!=son[fa[x]][1]);}
	inline void rotate(int x){
		int y=fa[x],z=fa[y],k=get(x),l=son[x][k^1];
		if(!isroot(y)) son[z][get(y)]=x;
		fa[x]=z,fa[y]=x,son[x][k^1]=y;
		fa[l]=y,son[y][k]=l;
	}
	inline void splay(int x){
		for(int re y=fa[x];!isroot(x);rotate(x),y=fa[x])
			if(!isroot(y)) rotate(get(y)==get(x)?y:x);
	}
	inline int getpos(int x){
		while(son[x][0]) x=son[x][0];
		return x;
	}
	inline void access(int x){
		for(int re y=0,pos;x;y=x,x=fa[x]){
			splay(x),pos=getpos(son[x][1]);
			if(pos) SGT::update(1,L[pos],R[pos],1);
			son[x][1]=y,pos=getpos(y);
			if(pos) SGT::update(1,L[pos],R[pos],-1);
		}
	}
}

inline void add(int u,int v){Next[++cnt]=Head[u],V[cnt]=v,Head[u]=cnt;}
void dfs1(int u,int f){
	siz[u]=1,son[u]=0,fa[u]=f,dep[u]=dep[f]+1,LCT::fa[u]=f;
	for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]])
		if(v!=f){dfs1(v,u),siz[u]+=siz[v];if(siz[v]>siz[son[u]]) son[u]=v;}
}
void dfs2(int u,int tp){
	t[L[u]=++tot]=u,top[u]=tp;
	if(son[u]) dfs2(son[u],tp);
	for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]])
		if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
	R[u]=tot;
}

inline int lca(int u,int v){
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		u=fa[top[u]];
	}return (dep[u]<dep[v])?u:v;
}

int main(){
//	freopen("2301.in","r",stdin);
	n=gi(),m=gi();
	for(int re i=1;i< n;++i) u=gi(),v=gi(),add(u,v),add(v,u);
	dfs1(1,0),dfs2(1,1),SGT::build(1,1,n);
	while(m--){
		op=gi();
		if(op==1) u=gi(),LCT::access(u);
		if(op==2) u=gi(),v=gi(),g=lca(u,v),printf("%d\n",val(1,L[u])+val(1,L[v])-2*val(1,L[g])+1);
		if(op==3) u=gi(),printf("%d\n",SGT::query_max(1,L[u],R[u]));
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值