[BZOJ4817][LCT][线段树]SDOI2017:树点涂色

BZOJ4817

分析:仔细看一下第一个操作像什么(看标题)
那么我们在access的过程中把点x的原来的实儿子的子树权值+1,新的实儿子的子树权值-1(可以画图感性理解一下,挂在x下面的权值会+1,新的子树权值会-1)
然后第二个操作就维护一个dep
第三个操作就是查询子树dep最大值
那么开一个线段树维护dep,用树上两点间距离公式来求就好了

P.S.大家code的时候一定记得随时ctrl+s一下,又不麻烦,我码完namespace LCT之后电脑蓝屏了,我就只有又码了3k(mmp)

Code:

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=1e5+5;
namespace LCA{
	int vis[N<<1],head[N<<1],nxt[N<<1],tot=0;
	inline void add(int x,int y){vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;}
	int dep[N],ff[N],pt[N],siz[N],hson[N];
	void dfs1(int v){
		pt[v]=siz[v]=1;
		for(int i=head[v];i;i=nxt[i]){
			int y=vis[i];
			if(pt[y]) continue;
			ff[y]=v;dep[y]=dep[v]+1;
			dfs1(y);
			siz[v]+=siz[y];
			if(siz[y]>siz[hson[v]]) hson[v]=y;
		}
	}
	int dfn[N],id[N],top[N],sign=0;
	void dfs2(int v){
		dfn[v]=++sign;id[sign]=v;
		if(hson[v]) top[hson[v]]=top[v],dfs2(hson[v]);
		for(int i=head[v];i;i=nxt[i])
			if(!top[vis[i]]) top[vis[i]]=vis[i],dfs2(vis[i]);
	}
	inline int lca(int x,int y){
		while(top[x]!=top[y])
			dep[top[x]]>dep[top[y]]?x=ff[top[x]]:y=ff[top[y]];
		return dep[x]<dep[y]?x:y;
	}
}
using namespace LCA;
namespace segtree{
	struct seg{int l,r,mx,add;}tr[N<<2];
	#define ls tr[k].l
	#define rs tr[k].r
	#define mid ((ls+rs)>>1)
	inline void pushup(int k){tr[k].mx=max(tr[k<<1].mx,tr[k<<1|1].mx);}
	inline void pushadd(int k,int v){tr[k].mx+=v;tr[k].add+=v;}
	inline void pushdown(int k){
		if(tr[k].add){
			pushadd(k<<1,tr[k].add);
			pushadd(k<<1|1,tr[k].add);
			tr[k].add=0;
		}
	}
	void build(int k,int l,int r){
		ls=l,rs=r,tr[k].add=0;
		if(l==r) {tr[k].mx=dep[id[l]];return;}
		build(k<<1,l,mid),build(k<<1|1,mid+1,r);
		pushup(k);
	}
	void modify(int k,int ql,int qr,int v){
		if(rs<ql || ls>qr) return;
		if(ql<=ls && rs<=qr) return pushadd(k,v);
		pushdown(k);
		if(qr<=mid) modify(k<<1,ql,qr,v);
		else if(ql>mid) modify(k<<1|1,ql,qr,v);
		else modify(k<<1,ql,mid,v),modify(k<<1|1,mid+1,qr,v);
		pushup(k);
	}
	int query(int k,int ql,int qr){
		if(rs<ql || ls>qr) return 0;
		if(ql<=ls && rs<=qr) return tr[k].mx;
		pushdown(k);
		if(qr<=mid) return query(k<<1,ql,qr);
		else if(ql>mid) return query(k<<1|1,ql,qr);
		else return max(query(k<<1,ql,mid),query(k<<1|1,mid+1,qr));
	}
	inline int qpoint(int x){return query(1,dfn[x],dfn[x]);}
	inline int qpath(int x,int y){return qpoint(x)+qpoint(y)-2*qpoint(lca(x,y));}
	#undef ls
	#undef rs
}
using namespace segtree;
namespace LCT{
	int ls[N],rs[N],fa[N];
	inline int isrs(int x){return rs[fa[x]]==x;}
	inline bool isroot(int x){
		if(!x) return true;
		return ls[fa[x]]!=x && rs[fa[x]]!=x;
	}
	inline void rotate(int x){
		int y=fa[x],z=fa[y],b=ls[y]==x?rs[x]:ls[x];
		if(z && !isroot(y)) (ls[z]==y?ls[z]:rs[z])=x;
		fa[x]=z,fa[y]=x;b?fa[b]=y:0;
		if(ls[y]==x) rs[x]=y,ls[y]=b;
		else ls[x]=y,rs[y]=b;
	}
	inline void splay(int x){
		while(!isroot(x)){
			while(!isroot(fa[x])){
				if(isrs(x)==isrs(fa[x])) rotate(fa[x]);
				else rotate(x);
			}
			rotate(x);
		}
	}
	inline void access(int x){
		for(int y=0;x;y=x,x=fa[x]){
			splay(x);
			if(rs[x]){
				int t=rs[x];while(ls[t]) t=ls[t];
				modify(1,dfn[t],dfn[t]+siz[t]-1,1);				
			}
			if(y){
				int t=y;while(ls[t]) t=ls[t];
				modify(1,dfn[t],dfn[t]+siz[t]-1,-1);
			}
			rs[x]=y;
		}
	}
}
using namespace LCT;
int main(){
	int n=read(),m=read();
	for(int x,y,i=1;i<n;i++) x=read(),y=read(),add(x,y),add(y,x);
	dep[1]=1,dfs1(1),top[1]=1,dfs2(1),build(1,1,n);
	for(int i=2;i<=n;i++) fa[i]=ff[i];
	while(m--){
		int op=read();
		if(op==1){int x=read();access(x);}
		else if(op==2){
			int x=read(),y=read();
			cout<<qpath(x,y)+1<<"\n";
		}
		else{
			int x=read();
			cout<<query(1,dfn[x],dfn[x]+siz[x]-1)<<"\n";
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值