题意
- 给你一颗树上每个点的 a c c e s s access access次数,计算轻重链切换次数的最大值。
首先要发现一个性质,那就是一个点的选择是无后效性的,就是对于每个点贡献的时候,只跟它的所有子树的大小有关,然后每个点的贡献就可以这样算 f u = v a l u + ∑ f a v = x f v , f_u=val_u+\sum_{fa_v=x}f_v, fu=valu+∑fav=xfv,其中 v a l u = m i n ( S i z e u − 1 , ( S i z e u − m a x S i z e v ) × 2 ) val_u=min(Size_u-1,(Size_u-maxSize_v)×2) valu=min(Sizeu−1,(Sizeu−maxSizev)×2)这个东西举几个栗子自己感性理解一下就好了,那么这样子我们就可以获得不带修改的 30 p t s 30pts 30pts了。
现在我们来考虑带修改怎么做,我们定义一个点的所有儿子中,满足 2 × S i z e v > S i z e u + 1 2×Size_v>Size_u+1 2×Sizev>Sizeu+1的儿子为实儿子,显然这样的儿子只有一个,或者没有。我们类比树链剖分复杂度的证明可以发现,一个点到根节点只有 log n \log n logn条轻边,那么我们可以用 L i n k C u t T r e e LinkCutTree LinkCutTree来维护这棵树,每次修改的时候暴力往上找到所有轻边即可,为什么这样复杂度是对的呢,因为题目的修改只有给一个权值加上一个数,那么如果它原来是实儿子现在仍旧是实儿子而不会变成虚儿子,那么这样子一个点的实儿子变化的时候就不用去遍历它的每一个儿子来考虑,因为它只能从虚儿子变成实儿子,那么就可以了,复杂度 O ( n log n ) O(n\log n) O(nlogn)。
有一些需要注意的地方,就修改的时候为了方便把当前点的贡献存下来,更新的时候减去当前点贡献加上修改后的贡献,还有就是要维护一个点子树和减去实儿子和自己权值的值,因为这样的话可以快速维护修改,又因为一个点在 l c t lct lct中的左儿子是在原树中自己的祖先,所以计算子树和的时候一定要减去左儿子权值,然后就是裸的模板了。
#include <bits/stdc++.h>
#define pb push_back
#define ll long long
#define ls(x) (ch[x][0])
#define rs(x) (ch[x][1])
#define sx (S[x] - S[ls(x)])
#define rel(x) (x == rs(fa[x]))
#define isroot(x) (ls(fa[x]) ^ x && rs(fa[x]) ^ x)
using namespace std;
const int N = 4e5 + 10;
vector<int> G[N];
ll ans, S[N], Rs[N], res[N], a[N];
int ch[N][2], fa[N], n, q;
void pushup(int x) {
S[x] = S[ls(x)] + S[rs(x)] + Rs[x] + a[x];
}
void rotate(int x) {
int dad = fa[x], f = rel(x);
if (!isroot(dad)) ch[fa[dad]][rel(dad)] = x;
fa[x] = fa[dad], ch[dad][f] = ch[x][f ^ 1];
fa[ch[dad][f]] = dad, ch[x][f ^ 1] = dad;
pushup(dad), pushup(fa[dad] = x);
}
void splay(int x) {
for (; !isroot(x); rotate(x)) if (!isroot(fa[x]))
rotate(rel(x) == rel(fa[x]) ? fa[x] : x);
}
void update(int x) {
ans -= res[x];
res[x] = rs(x) ? (sx - S[rs(x)]) * 2 : sx - 1;
if ((a[x] << 1) > sx + 1) res[x] = (sx - a[x]) << 1;
ans += res[x];
}
void access(int x, int z) {
for (int y = 0; x; update(x), x = fa[y = x]) {
splay(x), S[x] += z, *((y ? Rs : a) + x) += z;
if ((S[rs(x)] << 1) < sx + 1)
Rs[x] += S[rs(x)], rs(x) = 0;
if ((S[y] << 1) > sx + 1)
rs(x) = y, Rs[x] -= S[rs(x)];
}
printf("%lld\n", ans);
}
void dfs(int x, int dad) {
int now = 0; fa[x] = dad, S[x] = a[x];
for (auto v : G[x]) if (v ^ dad) {
dfs(v, x), S[x] += S[v];
if (S[v] > S[now]) now = v;
}
if ((S[now] << 1) > S[x] + 1) rs(x) = now;
Rs[x] = S[x] - a[x] - S[rs(x)], update(x);
}
int main() {
#ifdef ylsakioi
freopen("2434.in", "r", stdin);
freopen("2434.out", "w", stdout);
#endif
int x, y;
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; ++ i)
scanf("%lld", &a[i]);
for (int i = 1; i < n; ++ i) {
scanf("%d%d", &x, &y);
G[x].pb(y), G[y].pb(x);
}
dfs(1, 0), printf("%lld\n", ans);
while (q --) scanf("%d%d", &x, &y), access(x, y);
return 0;
}