最近公共祖先
考虑一个问题:任意两个点的\(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]);
}
}
}
}