题目大意
n个节点的有根树,根为1,每个节点有黑白两种颜色与一个固定的权值val。一开始全都是白色的,然后给出m个操作。有两种:
M v,把v修改成黑色;
Q v,找一个黑色节点u,使得u,v的lca的权值最大,输出权值,假如没有输出-1。
分析
先考虑一下暴力怎么打。
我们可以枚举lca是什么,看看其子树有没有黑色节点。
那么lca一定是v到根上的一个点,枚举lca,并查询lca的子树下,除了包含v的那个子树的其他节点有没有有黑色节点。那么设一个点x为根的子树有sum[x]个黑色节点。只要使得sum[x]-sum[son_with_v]>0,x就是一个合法的lca,可以对答案进行贡献。
那么我们查询和修改就沿着v往上跑到根就行了。这个复杂度是O(m*树高)的,可以过40分。
这样不行啊,怎么办呢,我们令S[x]=sum[dad[x]]-sum[x],那么一个点的S[x]>0,它的贡献就是val[dad[x]]。然后显然发现,S[x]从0变成1的时候才对以后询问有影响,而s[x]是不下降的,所以只要处理得当,我们能够省去很多无用修改。
那么想到这里就很显然用树链剖分维护会很好。
然而比赛的时候只剩40min了,打了个暴力就扫雷去了····
树链剖分很麻烦,又慢又难打又多细节,为了偷懒就换个方向吧。我们考虑一个LCA对子树造成的影响。假如 LCA某棵子树下有个点变成黑色,会发生什么?
1,原本LCA为根的整颗子树都没有白色,现在有个点黑了,整颗子树必须经过LCA到黑点的点到时候查询的时候都能有一个val[LCA],就是除了有黑点的子树,LCA的子树里的其他点全都可以对val[LCA]取max。
2,原本LCA为根的整颗子树有一个黑色的节点了。假如两个黑点的简单路径一定经过LCA的话,LCA的整颗子树都能对val[LCA]取max,这样子,LCA已经对子树内所有点都贡献了,LCA没用了,以后不用管他了。若不经过,那就不管它。
得出一个以点x为根的子树的有用状态:全白,有一个黑点,有多个黑点。
与上面S[x]类似,黑点数量只递增,x改变状态的次数最多2次,那么整棵树状态改变量O(n)。
类似的,我们从v往上跳,注意,假如跳到x时,不能改变dad[x]的状态,那我们就不继续跳了,因为再跳也没有用。然后改变状态的时候用线段树+dfs序维护每个点的最大值,查询直接单点查询就行了。
代码
#include<cstdio>
#include<algorithm>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
typedef long long ll;
const int N=200005;
int first[N],next[N],b[N],sum[N],a[N],dis[N],fa[N],tt,st[N],en[N],son[N],t1;
int tag[N*3];
int x,y,i,j,k,n,m,ans;
char s[20];
void cr(int x,int y)
{
tt++;
b[tt]=y;
next[tt]=first[x];
first[x]=tt;
}
void dfs(int x,int y)
{
st[x]=++t1;
dis[x]=dis[y]+1;
fa[x]=y;
sum[x]=0;
for(int p=first[x];p;p=next[p])
if (b[p]!=y)
dfs(b[p],x);
en[x]=t1;
}
void down(int x,int l,int r)
{
if (l==r) return;
tag[x*2]=max(tag[x*2],tag[x]);
tag[x*2+1]=max(tag[x*2+1],tag[x]);
}
void change(int x,int l,int r,int i,int j,int val)
{
if (j<i) return;
if (l==i&&r==j)
{
tag[x]=max(tag[x],val);
down(x,l,r);
return;
}
down(x,l,r);
int m=(l+r)/2;
if (m>=j)
change(x*2,l,m,i,j,val);
else
if (m<i)
change(x*2+1,m+1,r,i,j,val);
else
{
change(x*2,l,m,i,m,val);
change(x*2+1,m+1,r,m+1,j,val);
}
}
int get(int x,int l,int r,int pos)
{
down(x,l,r);
if (l==r)
return tag[x];
int m=(l+r)/2;
if (m>=pos)
return get(x*2,l,m,pos);
else return get(x*2+1,m+1,r,pos);
}
int main()
{
int size = 512 << 20; // 256MB
char *p = (char*)malloc(size) + size;
__asm__("movl %0, %%esp\n" :: "r"(p));
freopen("lca.in","r",stdin);
freopen("lca.out","w",stdout);
scanf("%d %d",&n,&m);
fo(i,1,n) scanf("%d",a+i);
fo(i,1,n-1)
{
scanf("%d %d\n",&x,&y);
cr(x,y);
cr(y,x);
}
dfs(1,0);
fo(i,1,m)
{
scanf("%s %d\n",s+1,&x);
if (s[1]=='M')
{
change(1,1,n,st[x],en[x],a[x]);
while (fa[x])
{
if (!son[fa[x]])
{
change(1,1,n,st[fa[x]],st[x]-1,a[fa[x]]);
change(1,1,n,en[x]+1,en[fa[x]],a[fa[x]]);
son[fa[x]]=x;
x=fa[x];
continue;
}
else if (son[fa[x]]!=x)
change(1,1,n,st[son[fa[x]]],en[son[fa[x]]],a[fa[x]]);
break;
}
}else
{
ans=get(1,1,n,st[x]);
if (!ans) ans=-1;
printf("%d\n",ans);
}
}
return 0;
}