最近公共祖先

  考虑一个问题:任意两个点的\(LCA\)要么是他们的某个祖先,要么是其中某个点。
  那么可以分类讨论。
  其实可以发现,当某个点被改为黑色后,他的所有祖先对除了他所在的子树的节点的贡献只需计算一遍,就是说,如果之前某个祖先已经对另外的子树的节点取过\(max\)了,意思就是说他已经作为可能的\(LCA\)对他们贡献过答案了,就不用在重复计算了。
  因为考虑题目的要求其实就是对所有已有的黑点与当前点的\(LCA\)取权值大者,因此重复的\(LCA\)只要计算一次即可,就可以保证\(O(N)\)同时也记录下来了某个点他与黑点的\(LCA\)中的权值最大值。最后直接查询即可。
  同时,某个黑点也可能作为\(LCA\),那么就会用它去更新自己子树里的点。
  考虑一个点的字数里所有点的\(DFS\)序连续,就可以以\(DFS\)序建立线段树了。

代码
#include<bits/stdc++.h>
using namespace std;
namespace STD
{
	#define rr register
	typedef long long ll;
	typedef string str;
	const int inf=INT_MAX;
	const int N=100004;
	const int M=200000;
	int n,m;
	int w[N];
	int to[N<<1],dire[N<<1],head[N];
	bool black,use[N];
	template<typename T>
	T cmax(rr T x,rr T y){return x>y?x:y;}
	inline void add(int f,int t)
	{
		static int num=0;
		to[++num]=t;
		dire[num]=head[f];
		head[f]=num;
	}
	int read()
	{
		rr int x_read=0,y_read=1;
		rr char c_read=getchar();
		while(c_read<'0'||c_read>'9')
		{
			if(c_read=='-') y_read=-1;
			c_read=getchar();
		}
		while(c_read<='9'&&c_read>='0')
		{
			x_read=(x_read<<3)+(x_read<<1)+(c_read^48);
			c_read=getchar();
		}
		return x_read*y_read;
	}
	int size[N],id[N],fa[N];
	void dfs(int x)
	{
		static int num=0;
		id[x]=++num;
		size[x]=1;
		for(rr int i=head[x];i;i=dire[i])
		{
			if(to[i]==fa[x]) continue;
			fa[to[i]]=x;
			dfs(to[i]);
			size[x]+=size[to[i]];
		}
	}
	#define rc id<<1|1
	#define lc id<<1
	struct node
	{
		int maxn,lazy;
		node(){lazy=0,maxn=-inf;}
		node(int maxn_){maxn=maxn_,lazy=0;}
	};
	class sgt
	{
		private:
			node a[N<<2];
			void push_down(int);
		public:
			void insert(int,int,int,int,int,int);
			int query(int,int,int,int);
	}t;
	void sgt::push_down(int id)
	{
		if(!a[id].lazy) return;
		a[lc].maxn=cmax(a[id].lazy,a[lc].maxn);
		a[rc].maxn=cmax(a[id].lazy,a[rc].maxn);
		a[lc].lazy=cmax(a[id].lazy,a[lc].lazy);
		a[rc].lazy=cmax(a[id].lazy,a[rc].lazy);
		a[id].lazy=0;
	}
	void sgt::insert(int id,int l,int r,int st,int en,int val)
	{
		if(st<=l&&r<=en)
		{
			a[id].lazy=cmax(a[id].lazy,val);
			a[id].maxn=cmax(a[id].maxn,val);
			return;
		}
		int mid=(l+r)>>1;
		push_down(id);
		if(st<=mid) insert(lc,l,mid,st,en,val);
		if(mid<en) insert(rc,mid+1,r,st,en,val);
		a[id].maxn=max(a[lc].maxn,a[rc].maxn);
	}
	int sgt::query(int id,int l,int r,int pos)
	{
		if(l==r) return a[id].maxn;
		int mid=(l+r)>>1;
		push_down(id);
		if(pos<=mid) return query(lc,l,mid,pos);
		else return query(rc,mid+1,r,pos);
	}
	#undef lc
	#undef rc
};
using namespace STD;
int main()
{
	n=read(),m=read();
	for(rr int i=1;i<=n;i++) w[i]=read();
	for(rr int i=1;i<n;i++)
	{
		int u=read(),v=read();
		add(u,v),add(v,u);
	}
	dfs(1);
	while(m--)
	{
		str s="";
		cin>>s;
		int x=read();
		if(s=="Query")
		{
			if(!black){printf("-1\n");continue;}
			int ans=t.query(1,1,n,id[x]);
			printf("%d\n",ans);
		}
		else
		{
			black=1;
			t.insert(1,1,n,id[x],id[x]+size[x]-1,w[x]);
			int last=x;
			x=fa[x];
			while(!use[x]&&x)
			{
				use[x]=1;
				t.insert(1,1,n,id[last]+size[last],id[x]+size[x]-1,w[x]);
				if(id[x]<=id[last]-1)
					t.insert(1,1,n,id[x],id[last]-1,w[x]);
				last=x;
				x=fa[x];
			}
			if(x)
			{
				t.insert(1,1,n,id[last]+size[last],id[x]+size[x]-1,w[x]);
				if(id[x]<=id[last]-1)
					t.insert(1,1,n,id[x],id[last]-1,w[x]);
			}
		}
	}
}
posted @ 2021-08-03 21:07  Geek_kay  阅读(40)  评论(0编辑  收藏  举报