题目链接:
http://132.232.5.128:666/submission/6690
题目大意:
给定一棵 n n n个结点的有根树,结点编号为 1 ∼ n 1 \sim n 1∼n,其中根节点为 1 1 1号结点。每个结点都对应着一种颜色(黑/白)和一个固定的权值,初始时所有结点的颜色都为白色。现在你需要实现以下两种操作:
- M o d i f y v Modify\ v Modify v:将结点 v v v的颜色修改为黑色;
- Q u e r y v Query\ v Query v:找到一个黑色结点 u u u,使得结点 u u u和 v v v的最近公共祖先 z z z对应的权值尽可能大,输出 z z z的权值。如果此时树中不存在黑色结点,输出 − 1 −1 −1。
题目分析:
1.考虑一个点被涂成了黑色后的影响:
- 黑点的子树上的点的答案将会受到影响,答案取原来的值和当前黑点权值的 m a x max max
- 还会影响到其他所有点,答案取原来的值和这个点和黑点的 L C A LCA LCA的权值的 m a x max max
2.我们发现,更新其他点时,我们可以顺着黑点往上遍历他的父节点,把这些点全当成黑色点来对其子节点更新(和黑色点影响子树的修改方式相同)。
3.如果之前这个点已经被修改成了黑色,代表这个点及其祖先的权值都被使用过了,那么就不需要更新答案了。
4.当我们这样做后,发现时间复杂度为
O
(
n
2
)
O(n^2)
O(n2),所以我们可以根据
D
F
S
DFS
DFS序维护一颗线段树,将时间复杂度下降至
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。因为一颗子树在
D
F
S
DFS
DFS序下的编号是一段连续的区间。直接进行区间修改即可。
5.最后注意一个坑:你可以同时修改整个一段区间的值,但是绝对不能用线段树的两个子节点去更新父节点的值,整个一段区间可以分解为两个区间,但两个区间不能合并。部分答案无法代表整体的答案。
正解程序:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll maxn=100010,maxm=200010;
struct node
{
ll v;
ll next;
}e[maxm];
ll p[maxn],t=0;
void insert(ll u,ll v)
{
e[t].v=v;
e[t].next=p[u];
p[u]=t++;
}
ll n,m,val[maxn];
ll count1,fa[maxn],dfn[maxn],son[maxn];
void dfs(ll u)
{
dfn[u]=++count1;
son[u]=1;
for(ll i=p[u];i!=-1;i=e[i].next)
{
ll v=e[i].v;
if(v!=fa[u])
{
fa[v]=u;
dfs(v);
son[u]+=son[v];
}
}
}
ll tree[maxn<<2];
void change(ll pos,ll l,ll r,ll s,ll e,ll num)
{
if(num<tree[pos] || s>e)
return;
if(s<=l && r<=e)
{
tree[pos]=max(tree[pos],num);
return;
}
ll mid=(l+r)>>1;
if(s<=mid)
change(pos<<1,l,mid,s,e,num);
if(e>mid)
change(pos<<1|1,mid+1,r,s,e,num);
}
ll getans(ll pos,ll l,ll r,ll x)
{
if(l==r)
return tree[pos];
ll mid=(l+r)>>1;
if(x<=mid)
return max(tree[pos],getans(pos<<1,l,mid,x));
else
return max(tree[pos],getans(pos<<1|1,mid+1,r,x));
}
void work(ll start,ll end1,ll num)
{
if(end1==0)//处理子树
change(1,1,n,dfn[start],dfn[start]+son[start]-1,num);
else
{
change(1,1,n,dfn[start],dfn[end1]-1,num);
change(1,1,n,dfn[end1]+son[end1],dfn[start]+son[start]-1,num);
}
}
bool black[maxn];
int main()
{
memset(p,-1,sizeof(p));
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=n;i++)
scanf("%lld",&val[i]);
for(ll i=1;i<=n-1;i++)
{
ll u,v;
scanf("%lld%lld",&u,&v);
insert(u,v);
insert(v,u);
}
count1=0;
dfs(1);
memset(black,false,sizeof(black));
memset(tree,-1,sizeof(tree));
for(ll i=1;i<=m;++i)
{
char str[10];
ll point;
scanf("%s",str);
scanf("%lld",&point);
if(str[0]=='M')
{
for(ll last=0;point;last=point,point=fa[point])
{
work(point,last,val[point]);
if(black[point])
break;
black[point]=true;
}
}
else
printf("%lld\n",getans(1,1,n,dfn[point]));
}
return 0;
}