Decription
-
给定一棵树以及树上的每个节点的(access)次数,请合理安排(access)顺序以最大化虚实链的切换次数,需要支持单点修改。
Solution
首先考虑不带修改的情况:
对于节点(u),(u)点的虚实链切换次数至于(u)子树内的点有关,每个点是独立的,可以分开计算。而(u)节点的每次切换一定意味着出现了先后两个(access)来自(u)不同的子树,考虑最优策略,设(mx)为(u)的每个子树的(access)次数中最大的一个,(sum)为它们(access)次数的总和。
那么当(2mxleq sum)时,我们总能找到方法使得任意相邻的两个(access)都来自不同子树,贡献为(sum-1),否则最后几个只能是连续的(mx)了,贡献为(2(sum-mx)),因此总贡献就是(min(2(sum-mx),sum-1)),于是直接树形(DP)一遍即可。
接着考虑带修改的情况:
修改节点(u)时,显然只会影响它的祖先的贡献。
对于节点(u)以及它的儿子(v),如果(2s_v)((s_u)为(u)子树的(access)次数之和)(> s_u),因此(u)的贡献为(2(s_u-s_v)),那么接下来如果(v)子树中的点进行修改,增加了(w),(2(s_u-s_v))依然比(sum-1)大,(u)的贡献不会改变。这种情况下从(u)向(v)连一条实边,其他边为虚边,于是我们修改节点(x)时只会影响其到根路径上所有虚边的顶点的答案,也只会有可能将这些虚边变为实边。
而每条路径上的虚边数量一定(<log sum a),证明与树剖类似,至于找虚边则直接(access)即可。
Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=4e5+10;
int n,m,first[N],cnt,fa[N];
ll a[N],s[N],vs[N],ans;
struct node{
int v,nxt;
}e[N<<1];
inline void add(int u,int v){e[++cnt].v=v;e[cnt].nxt=first[u];first[u]=cnt;}
int ch[N][2];
inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
inline bool which(int x){return ch[fa[x]][1]==x;}
inline void pushup(int u){s[u]=vs[u]+s[ch[u][0]]+s[ch[u][1]]+a[u];}
inline void Rotate(int x){
int y=fa[x],z=fa[y],wx=which(x),wy=which(y);
fa[x]=z;if(!isroot(y)) ch[z][wy]=x;
ch[y][wx]=ch[x][wx^1];fa[ch[x][wx^1]]=y;
ch[x][wx^1]=y;fa[y]=x;
pushup(y);pushup(x);
}
inline void Splay(int x){
while(!isroot(x)){
int y=fa[x];
if(!isroot(y)) which(x)==which(y)?Rotate(y):Rotate(x);
Rotate(x);
}
pushup(x);
}
inline void solve(int u){
s[u]=a[u];
ll mx=a[u],pos=0;
for(int i=first[u];i;i=e[i].nxt){
int v=e[i].v;
if(v==fa[u]) continue;
fa[v]=u;solve(v);
s[u]+=s[v];
if(s[v]>mx) mx=s[v],pos=v;
}
ans+=min(s[u]-1,(s[u]-mx)<<1);
if((mx<<1)>=s[u]+1) ch[u][1]=pos;
vs[u]=s[u]-a[u]-s[ch[u][1]];
}
inline ll calc(int u,ll sum,ll mx){
return mx?(sum-mx)<<1:((a[u]<<1)>=sum+1)?(sum-a[u])<<1:sum-1;
}
inline void access(int x,ll w){
Splay(x);
ll sum=s[x]-s[ch[x][0]],mx=s[ch[x][1]];
ans-=calc(x,sum,mx);
a[x]+=w;sum+=w;
if((mx<<1)<sum+1) ch[x][1]=0,vs[x]+=mx,mx=0;
pushup(x);ans+=calc(x,sum,mx);
int last=x;x=fa[x];
for(;x;last=x,x=fa[x]){
Splay(x);
ll sum=s[x]-s[ch[x][0]],mx=s[ch[x][1]];
ll rec=ans;
ans-=calc(x,sum,mx);
s[x]+=w;sum+=w;vs[x]+=w;
if((mx<<1)<sum+1) ch[x][1]=0,vs[x]+=mx,mx=0;
if((s[last]<<1)>=sum+1) ch[x][1]=last,vs[x]-=s[last],mx=s[last];
pushup(x);ans+=calc(x,sum,mx);
}
}
此处省略了一个iobuff
int main(){
// freopen("b4.in","r",stdin);
read(n);read(m);
for(int i=1;i<=n;++i) read(a[i]);
for(int i=1,u,v;i<n;++i) read(u),read(v),add(u,v),add(v,u);
solve(1);
putint(ans,'
');ll w;
for(int i=1,x;i<=m;++i){
read(x);read(w);
access(x,w);
putint(ans,'
');
}
flush();
return 0;
}