版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/DT_Kang/article/details/81138582
题目大意:给定一棵树,以及每个点 access 的次数,求轻重链切换次数最大值。带修改,每次给每个点加 w 次。
考虑不带修改怎么做。可以发现,如果给 u 安排一个最优序列,那么给它的祖先安排顺序的时候是不会和它冲突的(因为考虑 u 的祖先的时候 u 中的每个点就是等价的了)。因此我们可以每个点单独计算。那么对于一个点 u,问题可以转化成有一些颜色的球,每种颜色有 个,求一种排列方式使得相邻且颜色不同的颜色最多。容易发现,如果每种球数量比较平均,答案就是球的数量减一。如果有一种球大于总数的一半,那么答案是 。
考虑带修改。发现我们不需要关注那些已经超过子树一半的点。如果我们把每个点和它的“重儿子”连实边,和它其他的儿子连虚边,那么我们修改影响的一定只有轻边。而且证明,一个点到根的路径上经过不超过 条轻边。用 LCT 维护这个过程:找到一条虚边,更改信息,检查是否需要更改连边。
这里我们需要用 LCT 维护子树信息。做法是这样的:额外记录一下一个点所有“虚儿子”(fa 数组指向他的儿子)的信息。update 的时候用 splay 里的左右儿子的总信息和自己的虚儿子的信息和自己的信息合并。事实上,这样每个点的总信息表示的其实是他所在的 splay 的子树中的每个点的子树信息。
细节挺多的。
#include<cstdio>
#include<iostream>
#define ll long long
using namespace std;
struct edge{
int to,next;
}ed[800010];
int fa[400010],opt[400010],head[400010],sz,son[400010][2];
ll sum[400010],sumi[400010],ans,a[400010];
void add_edge(int from,int to)
{
ed[++sz].to=to;
ed[sz].next=head[from];
head[from]=sz;
}
inline int read()
{
int x=0,flag=1;char c=getchar();
while(!isdigit(c)) {if(c=='-') flag=-1;c=getchar();}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*flag;
}
bool not_root(int x)
{
if(son[fa[x]][0]==x||son[fa[x]][1]==x) return true;
return false;
}
void push_up(int x)
{
sum[x]=sum[son[x][0]]+sum[son[x][1]]+a[x]+sumi[x];
}
void rotate(int x)
{
int y=fa[x],z=fa[y],kind= x==son[y][0];
int tmp=not_root(y);
son[y][!kind]=son[x][kind];
if(son[x][kind]) fa[son[x][kind]]=y;
son[x][kind]=y;
fa[y]=x;
fa[x]=z;
if(tmp)
{
if(y==son[z][0]) son[z][0]=x;
else son[z][1]=x;
}
push_up(y);
push_up(x);
}
void splay(int x)
{
while(not_root(x))
{
int y=fa[x],z=fa[y];
if(not_root(y))
{
if((x==son[y][0])^(y==son[z][0])) rotate(x);
else rotate(y);
}
rotate(x);
}
push_up(x);
}
void access(int x,int w)
{
for(int y=0;x;x=fa[y=x])
{
splay(x);
ll tmp=sum[x]-sum[son[x][0]];
if(opt[x]==0) ans-=tmp-1;
else if(opt[x]==1) ans-=(tmp-sum[son[x][1]])*2;
else ans-=(tmp-a[x])*2;
tmp+=w,sum[x]+=w;
if(y) sumi[x]+=w;
else a[x]+=w;
if(sum[y]*2>tmp+1) sumi[x]+=sum[son[x][1]],sumi[x]-=sum[son[x][1]=y];
if(sum[son[x][1]]*2>tmp+1) opt[x]=1,ans+=2*(tmp-sum[son[x][1]]);
else
{
if(son[x][1]) sumi[x]+=sum[son[x][1]],son[x][1]=0;
if(a[x]*2>tmp+1) opt[x]=2,ans+=2*(tmp-a[x]);
else opt[x]=0,ans+=tmp-1,son[x][1]=0;
}
}
}
void dfs(int u,int fff)
{
sum[u]=a[u];
ll Max=a[u],p=u;
for(int i=head[u];i;i=ed[i].next)
{
int v=ed[i].to;
if(v==fff) continue;
fa[v]=u;
dfs(v,u);
sumi[u]+=sum[v];
if(sum[v]>Max) Max=sum[v],p=v;
}
sum[u]+=sumi[u];
if(Max*2>sum[u]+1)
{
ans+=2*(sum[u]-Max);
if(p!=u) opt[u]=1,sumi[u]-=sum[p],son[u][1]=p;
else opt[u]=2;
}
else ans+=sum[u]-1;
}
int main()
{
int n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<n;i++)
{
int u=read(),v=read();
add_edge(u,v);
add_edge(v,u);
}
dfs(1,1);
cout<<ans<<endl;
for(int i=1;i<=m;i++)
{
int u=read(),w=read();
access(u,w);
cout<<ans<<endl;
}
return 0;
}