【洛谷P3703】树点涂色

题目

题目链接:https://www.luogu.com.cn/problem/P3703
Bob 有一棵 (n) 个点的有根树,其中 (1) 号点是根节点。Bob 在每个点上涂了颜色,并且每个点上的颜色不同。
定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。
Bob 可能会进行这几种操作:

  • 1 x 表示把点 (x) 到根节点的路径上所有的点染上一种没有用过的新颜色。
  • 2 x y(x)(y) 的路径的权值。
  • 3 x 在以 (x) 为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。

Bob 一共会进行 (m) 次操作。
(1leq n leq 10^5)(1leq m leq 10^5)

思路

发现每次修改都是直接把 (x) 到根的路径的颜色全部赋值,然后原来的颜色就失效了,容易发现任意时刻每一个节点最多有一个儿子的颜色与其相同。
发现这个操作和 LCT 的 access 十分相似,每一个点只可以保留一条实边。
如果我们把操作一看做将 access((x)),那么一个点的权值就转换为了该点到根节点的虚边的数量。
在 access 的时候,当我们把 (x) 与其原来的实儿子的边断开时,它实儿子所在子树的权值全部加 (1),连接新的实儿子 (y) 后,(y) 所在子树内的权值全部减 (1)
那么用一个线段树维护区间和以及区间最大值即可。注意是一个节点所在子树内的权值加减,所以要先 findrt,然后在找到的根节点进行修改。但是 findrt 之后不能 splay,否则会改变 Splay 的形态。
然后对于操作 (2),答案即为 (x,y) 的权值和减去他们 LCA 的权值的两倍再加上 (1)。操作三直接区间最大值即可。
时间复杂度 (O(mlog^2n))

代码

#include <bits/stdc++.h>
using namespace std;

const int N=100010,LG=18;
int n,m,tot,head[N],dfn[N],rk[N],siz[N],dep[N],f[N][LG+1];

struct edge
{
	int next,to;
}e[N*2];

void add(int from,int to)
{
	e[++tot]=(edge){head[from],to};
	head[from]=tot;
}

struct SegTree
{
	int maxn[N*4],lazy[N*4];
	
	void pushdown(int x)
	{
		if (lazy[x])
		{
			maxn[x*2]+=lazy[x]; lazy[x*2]+=lazy[x];
			maxn[x*2+1]+=lazy[x]; lazy[x*2+1]+=lazy[x];
			lazy[x]=0;
		}
	}
	
	void pushup(int x)
	{
		maxn[x]=max(maxn[x*2],maxn[x*2+1]);
	}
	
	void build(int x,int l,int r)
	{
		if (l==r)
		{
			maxn[x]=dep[rk[l]];
			return;
		}
		int mid=(l+r)>>1;
		build(x*2,l,mid); build(x*2+1,mid+1,r);
		pushup(x);
	}
	
	void update(int x,int l,int r,int ql,int qr,int v)
	{
		if (ql<=l && r<=qr)
		{
			lazy[x]+=v; maxn[x]+=v;
			return;
		}
		pushdown(x);
		int mid=(l+r)>>1;
		if (ql<=mid) update(x*2,l,mid,ql,qr,v);
		if (qr>mid) update(x*2+1,mid+1,r,ql,qr,v);
		pushup(x);
	}
	
	int query(int x,int l,int r,int ql,int qr)
	{
		if (!l || !r) return 0;
		if (ql<=l && r<=qr) return maxn[x];
		pushdown(x);
		int mid=(l+r)>>1,s=0;
		if (ql<=mid) s=max(s,query(x*2,l,mid,ql,qr));
		if (qr>mid) s=max(s,query(x*2+1,mid+1,r,ql,qr));
		return s;
	}
}seg;

struct LCT
{
	int fa[N],ch[N][2];
	
	bool pos(int x)
	{
		return x==ch[fa[x]][1];
	}
	
	bool notrt(int x)
	{
		return x==ch[fa[x]][1] || x==ch[fa[x]][0];
	}
	
	void rotate(int x)
	{
		int y=fa[x],z=fa[y],k=pos(x),c=ch[x][k^1];
		if (notrt(y)) ch[z][pos(y)]=x; ch[x][k^1]=y; ch[y][k]=c;
		if (c) fa[c]=y; fa[y]=x; fa[x]=z;
	}
	
	void splay(int x)
	{
		while (notrt(x))
		{
			int y=fa[x];
			if (notrt(y))
				rotate(pos(x)==pos(y)?y:x);
			rotate(x);
		}
	}
	
	int findrt(int x)
	{
		splay(x);
		for (;ch[x][0];x=ch[x][0]) ;
		return x;
	}
	
	void access(int x)
	{
		for (int y=0;x;y=x,x=fa[x])
		{
			splay(x);
			int z=ch[x][1]; ch[x][1]=0;
			if (z)
			{
				int p=findrt(z);
				seg.update(1,1,n,dfn[p],dfn[p]+siz[p]-1,1);
			}
			if (y)
			{
				int p=findrt(y);
				seg.update(1,1,n,dfn[p],dfn[p]+siz[p]-1,-1);
			}
			ch[x][1]=y;
		}
	}
}lct;

void dfs(int x,int fa)
{
	lct.fa[x]=fa;
	siz[x]=1; dfn[x]=++tot; rk[tot]=x;
	f[x][0]=fa; dep[x]=dep[fa]+1;
	for (int i=1;i<=LG;i++)
		f[x][i]=f[f[x][i-1]][i-1];
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (v!=fa)
		{
			dfs(v,x);
			siz[x]+=siz[v];
		}
	}
}

int lca(int x,int y)
{
	if (dep[x]<dep[y]) swap(x,y);
	for (int i=LG;i>=0;i--)
		if (dep[f[x][i]]>=dep[y]) x=f[x][i];
	if (x==y) return x;
	for (int i=LG;i>=0;i--)
		if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	for (int i=1,x,y;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y); add(y,x);
	}
	tot=0; dfs(1,0);
	seg.build(1,1,n);
	while (m--)
	{
		int opt,x,y;
		scanf("%d",&opt);
		if (opt==1)
		{
			scanf("%d",&x);
			lct.access(x);
		}
		if (opt==2)
		{
			scanf("%d%d",&x,&y);
			int p=lca(x,y);
			int s1=seg.query(1,1,n,dfn[x],dfn[x])+seg.query(1,1,n,dfn[y],dfn[y]);
			int s2=2*seg.query(1,1,n,dfn[p],dfn[p]);
			printf("%d
",s1-s2+1);
		}
		if (opt==3)
		{
			scanf("%d",&x);
			printf("%d
",seg.query(1,1,n,dfn[x],dfn[x]+siz[x]-1));
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/14245594.html