[BZOJ4817][SDOI2017]树点涂色

BZOJ
Luogu

sol

这个题只要你想清楚了就变成码农题了。
你会发现操作1神似(LCT)中的(access)
考虑在某一次1操作中,会有若干组父子关系,有的是父子从同色变成了异色,有的是从异色变成了同色。如果我们就把同色的点放在一棵splay里面,那操作就直接对应(access)操作了。
我们考虑维护这个东西:每个点到根的路径权值(val_i)
如果一对父子的颜色从同色变成异色,那么原树中以那个儿子为根的子树的每个点的权值都会加一;反之,若从异色变同色,那么就会全部减一。而对应到(access)中,由同变异的刚好是原实儿子,由异变同的刚好是新实儿子。
子树转化成区间,然后就是经典线段树操作。
对于操作2,答案是(val_x+val_y-2*val_{lca(u,v)}+1)
所以你需要写LCA+LCT+支持区间加和区间查最大值的线段树

注意:在(access)中,打标记的位置是链顶
所以要在splay中暴跳一下左儿子找到最左边那个点

code

LCA沉迷树链剖分无法自拔
线段树可以标记永久化
LCT只要写(access),连(rev)都不用打

#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 100005;
int gi()
{
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
struct edge{int to,next;}a[N<<1];
int n,m,head[N],cnt,pa[N],dep[N],sz[N],son[N],top[N],dfn[N],low[N];
void dfs1(int u,int f)
{
	pa[u]=f;dep[u]=dep[f]+1;sz[u]=1;
	for (int e=head[u];e;e=a[e].next)
	{
		int v=a[e].to;if (v==f) continue;
		dfs1(v,u);
		sz[u]+=sz[v];if (sz[v]>sz[son[u]]) son[u]=v;
	}
}
void dfs2(int u,int up)
{
	top[u]=up;dfn[u]=++cnt;
	if (son[u]) dfs2(son[u],up);
	for (int e=head[u];e;e=a[e].next)
		if (a[e].to!=pa[u]&&a[e].to!=son[u])
			dfs2(a[e].to,a[e].to);
	low[u]=cnt;
}
int getlca(int u,int v)
{
	while (top[u]^top[v])
	{
		if (dep[top[u]]<dep[top[v]]) swap(u,v);
		u=pa[top[u]];
	}
	return dep[u]<dep[v]?u:v;
}
int tag[N<<2],mx[N<<2];
void Modify(int x,int l,int r,int ql,int qr,int v)
{
	if (l>=ql&&r<=qr)
	{
		tag[x]+=v;mx[x]+=v;
		return;
	}
	int mid=l+r>>1;
	if (ql<=mid) Modify(x<<1,l,mid,ql,qr,v);
	if (qr>mid) Modify(x<<1|1,mid+1,r,ql,qr,v);
	mx[x]=max(mx[x<<1],mx[x<<1|1])+tag[x];
}
int Query(int x,int l,int r,int ql,int qr)
{
	if (l>=ql&&r<=qr) return mx[x];
	int mid=l+r>>1,s=-1e9;
	if (ql<=mid) s=max(s,Query(x<<1,l,mid,ql,qr));
	if (qr>mid) s=max(s,Query(x<<1|1,mid+1,r,ql,qr));
	return s+tag[x];
}
int fa[N],ls[N],rs[N];
bool isroot(int x){return ls[fa[x]]!=x&&rs[fa[x]]!=x;}
void R_rotate(int x)
{
	int y=fa[x],z=fa[y];
	ls[y]=rs[x];
	if (rs[x]) fa[rs[x]]=y;
	fa[x]=z;
	if (y==ls[z]) ls[z]=x;if (y==rs[z]) rs[z]=x;
	rs[x]=y;fa[y]=x;
}
void L_rotate(int x)
{
	int y=fa[x],z=fa[y];
	rs[y]=ls[x];
	if (ls[x]) fa[ls[x]]=y;
	fa[x]=z;
	if (y==ls[z]) ls[z]=x;if (y==rs[z]) rs[z]=x;
	ls[x]=y;fa[y]=x;
}
void splay(int x)
{
	while (!isroot(x))
	{
		int y=fa[x],z=fa[y];
		if (isroot(y))
			if (x==ls[y]) R_rotate(x);
			else L_rotate(x);
		else
			if (y==ls[z])
				if (x==ls[y]) R_rotate(y),R_rotate(x);
				else L_rotate(x),R_rotate(x);
			else
				if (x==ls[y]) R_rotate(x),L_rotate(x);
				else L_rotate(y),L_rotate(x);
	}
}
int getf(int x){while(ls[x])x=ls[x];return x;}
void access(int x)
{
	int gg;
	for (int y=0;x;y=x,x=fa[x])
	{
		splay(x);
		if (rs[x]) gg=getf(rs[x]),Modify(1,1,n,dfn[gg],low[gg],1);
		if (y) gg=getf(y),Modify(1,1,n,dfn[gg],low[gg],-1);
		rs[x]=y;
	}
}
int main()
{
	n=gi();m=gi();
	for (int i=1,u,v;i<n;i++)
	{
		u=gi();v=gi();
		a[++cnt]=(edge){v,head[u]};head[u]=cnt;
		a[++cnt]=(edge){u,head[v]};head[v]=cnt;
	}
	dfs1(1,0);cnt=0;dfs2(1,1);
	for (int i=1;i<=n;i++) fa[i]=pa[i],Modify(1,1,n,dfn[i],low[i],1);
	while (m--)
	{
		int opt=gi(),x=gi(),y,gg;
		if (opt==1) access(x);
		if (opt==2)
		{
			y=gi();
			gg=getlca(x,y);
			printf("%d
",Query(1,1,n,dfn[x],dfn[x])+Query(1,1,n,dfn[y],dfn[y])-2*Query(1,1,n,dfn[gg],dfn[gg])+1);
		}
		if (opt==3) printf("%d
",Query(1,1,n,dfn[x],low[x]));
	}
	return 0;
}
原文地址:https://www.cnblogs.com/zhoushuyu/p/8334926.html