题意:一棵nnn个点的树,每个点有权值aia_iai,初始时给定。维护:
-
单点权值加上一个正数。
-
询问每个点恰好执行aia_iai次 access 操作,任意安排顺序的条件下切换轻重链总次数的最大值。
n≤4×105n\leq 4\times 10^5n≤4×105
一道奇怪的题(?)
对于一个点uuu,考虑它子树内两个相邻 access的点,它们在uuu这里切换了一次当且仅当它们属于 不同的 uuu的儿子 为根 的 子树。(为了方便,我们把uuu单独这一个点看成uuu的子树。)
把来自同一个子树内的一次access看成一种颜色的小球,我们希望相邻的不同色小球尽量多。
结论 设总小球数为nnn,颜色最多的小球个数为mmm,那么最大贡献为min{n−1,2(n−m)}\min\{n-1,2(n-m)\}min{n−1,2(n−m)}
证明
考虑把最多的颜色摆出来,在m−1m-1m−1个空隙中放其他颜色。
- 假设最多的颜色只有一种
把所有其他颜色的球依次往空隙中放,放完一个颜色继续往下一个空隙放,空隙放完了再从第一个开始。
如果空隙都放完了,即n−m≥m−1n-m\geq m-1n−m≥m−1,即n−1≤2(n−m)n-1\leq2(n-m)n−1≤2(n−m),所有相邻的小球颜色都不同,答案为n−1n-1n−1
否则颜色最多的球一定会相邻。考虑开始时贡献为000,插入一个任意颜色的球最多让贡献+2+2+2,上面的构造方法可以达到这个最大值,所以最大贡献为2(n−m)2(n-m)2(n−m)
- 最多的颜色有多种
显然这时m≤n2m\leq \frac{n}{2}m≤2n,把最多的颜色各取一个构成一组绑在一起,然后其他颜色用同样的方法放在组之间的空隙内,这样不会有同色相邻。
就可以O(n)O(n)O(n)处理了
然后考虑怎么用数据结构维护这玩意
上面已经推过了,虽然表达式看起来很奇怪,但实际上本质上是判断最大的子树是否超过自己的一半(这里的大小都是指aaa的和)
一点也不自然地想到实链剖分
具体而言,设sumusum_usumu表示uuu子树内权值和,对于uuu和它的儿子vvv,如果2sumv>sumu+12sum_v>sum_u+12sumv>sumu+1(先不管uuu本身什么的),那么vvv是uuu的重儿子,显然重儿子最多有一个。如果不存在这样的儿子就没有重儿子。
这样做的好处是重链上都是取的min{n−1,2(n−m)}\min\{n-1,2(n-m)\}min{n−1,2(n−m)}的右边这项,而把父亲和儿子加上同一个正数时左边会变大,右边不会变,而右边本来就比左边小,不仅仍然是重边,连这个点的答案都不会改变,直接跳过去就可以了。而到根的轻边是O(logV)O(\log V)O(logV)的,直接讨论一下轻边的情况即可。
具体而言就是魔改一下LCT的access,跳轻边的时候需要判一下。具体实现的时候很诡异,建议直接看代码。
注意复杂度是通过轻边条数而非finger-search保证的,所以必须在修改前dfs一遍处理出重儿子
复杂度应该是O(nlognlogV)O(n\log n\log V)O(nlognlogV)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
#define MAXN 400005
using namespace std;
typedef long long ll;
inline int read()
{int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans;
}
int ch[MAXN][2],fa[MAXN];
ll val[MAXN],lz[MAXN];
inline void pushlzy(int x,ll v){val[x]+=v,lz[x]+=v;}
inline void pushdown(int x)
{if (lz[x]){if (ch[x][0]) pushlzy(ch[x][0],lz[x]);if (ch[x][1]) pushlzy(ch[x][1],lz[x]);lz[x]=0;}
}
inline int get(int x){return ch[fa[x]][1]==x;}
inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
inline void rotate(int x)
{int y=fa[x],z=fa[y];int l=get(x),r=l^1;int w=ch[x][r];if (!isroot(y)) ch[z][get(y)]=x;ch[x][r]=y,ch[y][l]=w;if (w) fa[w]=y;fa[y]=x,fa[x]=z;
}
int q[MAXN],tp;
inline void splay(int x)
{q[tp=1]=x;for (int i=x;!isroot(i);i=fa[i]) q[++tp]=fa[i];for (int i=tp;i>=1;i--) pushdown(q[i]);while (!isroot(x)){int y=fa[x];if (!isroot(y)){if (get(x)==get(y)) rotate(y);else rotate(x);}rotate(x);}
}
ll res[MAXN],a[MAXN],ans;
inline int findrt(int x){while (ch[x][0]) pushdown(x),x=ch[x][0];return x;}
inline void access(int x,int v)
{a[x]+=v;for (int y=0;x;y=x,x=fa[x]){splay(x);val[x]+=v;if (ch[x][0]) pushlzy(ch[x][0],v);ans-=res[x];int t=findrt(ch[x][1]);if (2*(val[x]-val[t])>=val[x]-1) ch[x][1]=0;t=findrt(y);if (2*(val[x]-val[t])<val[x]-1) ch[x][1]=y;t=findrt(ch[x][1]);res[x]=(t? 2*(val[x]-val[t]):val[x]-1);if (2*a[x]>val[x]+1) res[x]=2*(val[x]-a[x]);ans+=res[x];}
}
vector<int> e[MAXN];
void dfs(int u)
{val[u]=a[u];for (int i=0;i<(int)e[u].size();i++)if (e[u][i]!=fa[u]){fa[e[u][i]]=u;dfs(e[u][i]);val[u]+=val[e[u][i]];}for (int i=0;i<(int)e[u].size();i++)if (e[u][i]!=fa[u]&&2*val[e[u][i]]>val[u]+1)ch[u][1]=e[u][i];res[u]=(ch[u][1]? 2*(val[u]-val[ch[u][1]]):val[u]-1);if (2*a[u]>val[u]+1) res[u]=2*(val[u]-a[u]);ans+=res[u];
}
int main()
{int n,m;n=read(),m=read();for (int i=1;i<=n;i++) a[i]=read();for (int i=1;i<n;i++){int u,v;u=read(),v=read();e[u].push_back(v),e[v].push_back(u);}dfs(1); printf("%lld\n",ans);while (m--){int x,v;x=read(),v=read();access(x,v);printf("%lld\n",ans);}return 0;
}