[SDOI2017]树点涂色

题目

抄题解.jpg

当是用来复习(LCT)的了

发现一操作很具有灵性,把一个点到根的路径涂上一种新颜色,这里的新颜色竟然和之前的都不同,那么所有的颜色必然都会是一段深度连续的路径了

既然是暴力到根,那么我们就会发现这个和(lct)(access)操作很像啊;而且每段颜色都是一段深度连续的路径,而一个(splay)里维护的也就是这些东西

于是我们可以直接使用(lct)来维护第一问,直接(access)上去把这个点到根的路径都搞到一个(splay)中去,这样一个(splay)里维护的就是颜色相同的一段路径

面对二操作,好像这是一个树剖操作啊,但是我们发现这里的同一颜色都是深度连续的,于是可以考虑使用树上差分,也就是(pre_x+pre_y-2 imes pre_{lca}+1)(+1)是因为(lca)被减了两次

考虑在(access)的同时维护出这个东西

回顾我们的(access)操作,板子里是这样写的

for(int y=0;x;y=x,f=fa[x])
	splay(x),ch[x][1]=y,pushup(x);

发现我们就是不断重复断掉(x)和之前的深度比它大的点之间的关系,连上一个新的儿子

断掉那个之前的深度更大的儿子,显然那个儿子就自己变成了一个(splay)的根了,这个(splay)以及下面的其余(splay)发现上面都出现了一个新的颜色,于是这个子树的(pre)都应该加(1)

连上新的儿子,新的儿子原来的那棵(splay)就消失了了,和当前的(splay)合并在一起了,于是我们应该把这棵子树的答案减(1)

于是我们(dfn)+线段树维护一下就好了

三操作就是查一个子树最大值,没了

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define re register
#define LL long long
const int maxn=100005;
inline int read() {
	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
struct E{int v,nxt;}e[maxn<<1];
inline int max(int a,int b) {return a>b?a:b;}
int ch[maxn][2],fa[maxn],f[maxn],deep[maxn],dfn[maxn],id[maxn];
int head[maxn],son[maxn],sum[maxn],top[maxn];
int l[maxn<<2],r[maxn<<2],tag[maxn<<2],mx[maxn<<2];
int n,m,num,__;
inline void add(int x,int y) {e[++num].v=y;e[num].nxt=head[x];head[x]=num;}
inline void pushup(int x) {mx[x]=max(mx[x<<1],mx[x<<1|1]);}
void build(int x,int y,int i) {
	l[i]=x,r[i]=y;
	if(x==y) {mx[i]=deep[id[x]];return;}
	int mid=x+y>>1;
	build(x,mid,i<<1);build(mid+1,y,i<<1|1);
	pushup(i);
}
inline void pushdown(int i) {
	if(!tag[i]) return;
	tag[i<<1]+=tag[i],tag[i<<1|1]+=tag[i];
	mx[i<<1]+=tag[i];mx[i<<1|1]+=tag[i];
	tag[i]=0;
}
void change(int x,int y,int i,int val) {
	if(x<=l[i]&&y>=r[i]) {
		mx[i]+=val;tag[i]+=val;
		return;
	}
	pushdown(i);
	int mid=l[i]+r[i]>>1;
	if(x<=mid) change(x,y,i<<1,val);
	if(y>mid) change(x,y,i<<1|1,val);
	pushup(i);
}
int query(int x,int y,int i) {
	if(x<=l[i]&&y>=r[i]) return mx[i];
	pushdown(i);
	int mid=l[i]+r[i]>>1,now=0;
	if(x<=mid) now=max(now,query(x,y,i<<1));
	if(y>mid) now=max(now,query(x,y,i<<1|1));
	return now;
}
int ask(int pos,int i) {
	if(l[i]==r[i]) return mx[i];
	pushdown(i);
	int mid=l[i]+r[i]>>1;
	if(pos<=mid) return ask(pos,i<<1);
	return ask(pos,i<<1|1); 
}
inline void Add(int x,int val) {change(dfn[x],dfn[x]+sum[x]-1,1,val);}
inline int nroot(int x) {return ch[fa[x]][0]==x||ch[fa[x]][1]==x;}
inline void rotate(int x) {
	int y=fa[x],z=fa[y],k=ch[y][1]==x,w=ch[x][k^1];
	if(nroot(y)) ch[z][ch[z][1]==y]=x;
	ch[x][k^1]=y,ch[y][k]=w;
	fa[w]=y,fa[y]=x,fa[x]=z;
}
inline void splay(int x) {
	while(nroot(x)) {
		int y=fa[x];
		if(nroot(y)) rotate((ch[y][1]==x)^(ch[fa[y]][1]==y)?x:y);
		rotate(x);
	}
}
inline int frt(int x) {
	while(ch[x][0]) x=ch[x][0];
	return x;
}
inline void access(int x) {
	for(re int y=0;x;y=x,x=fa[x]) {
		splay(x);
		if(ch[x][1]) Add(frt(ch[x][1]),1);
		if(y) Add(frt(y),-1);
		ch[x][1]=y;
	}
}
void dfs1(int x) {
	sum[x]=1;int maxx=-1;
	for(re int i=head[x];i;i=e[i].nxt) {
		if(deep[e[i].v]) continue;
		deep[e[i].v]=deep[x]+1;f[e[i].v]=fa[e[i].v]=x;
		dfs1(e[i].v);sum[x]+=sum[e[i].v];
		if(sum[e[i].v]>maxx) son[x]=e[i].v,maxx=sum[e[i].v];
	}
}
void dfs2(int x,int topf) {
	top[x]=topf;dfn[x]=++__,id[__]=x;
	if(!son[x]) return;
	dfs2(son[x],topf);
	for(re int i=head[x];i;i=e[i].nxt)
	if(!top[e[i].v]) dfs2(e[i].v,e[i].v);
}
inline int LCA(int x,int y) {
	while(top[x]!=top[y]) {
		if(deep[top[x]]<deep[top[y]]) std::swap(x,y);
		x=f[top[x]];
	}
	if(deep[x]<deep[y]) return x;return y;
}
int main() {
	n=read(),m=read();
	for(re int x,y,i=1;i<n;i++)
		x=read(),y=read(),add(x,y),add(y,x);
	deep[1]=1,dfs1(1),dfs2(1,1),build(1,n,1);
	int opt,x,y;
	while(m--) {
		opt=read(),x=read();
		if(opt==1) access(x);
		if(opt==2) 
			y=read(),printf("%d
",ask(dfn[x],1)+ask(dfn[y],1)-2*ask(dfn[LCA(x,y)],1)+1);
		if(opt==3) 
			printf("%d
",query(dfn[x],dfn[x]+sum[x]-1,1));
	}
	return 0;
}
原文地址:https://www.cnblogs.com/asuldb/p/10617262.html