主要思路
ZJOI 的题质量都好高啊!真的都是很棒的题。
其实这个覆盖的东西就相当于在 LCT 上进行 Access 时轻重链切换的次数。我们考虑静态的问题,对于点 \(u\) 来说,如果子树内有多个来自不同儿子子树内的子代节点,那么就需要进行合理的分配来把这个轻重链切换次数搞到最大。
转换一下就是两两不同颜色的点进行配对的最大值。设 \(S_0 = a_u, S_i = [\text{第 } i \text{个子树内的 access 次数之和}]\)。那么答案其实就是 \(\max\{ \sum_{i = 0}^m S_i – 1, 2(\sum_{i = 0}^m S_i – \max_{i \in [0, m]} S_i) \}\),前者是啥都不考虑,后者是连接到一个子节点。后者成为最大值的条件其实就是要成为重儿子。我们考虑维护这个东西。
发现其实我们用贪心的分配方案进行轻重链剖分时,复杂度也是对的。因为被贪心选择的那个子节点确实也是个重儿子,根据树链剖分那一套理论这个复杂度是成立的。
当我们进行修改时,我们就需要用 Access 操作来进行判断和修改。我们需要判断是否存在虚子树中的点成为重儿子,然后进行切换。
代码
// P4338.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 4e5 + 200;
typedef long long ll;
int n, q, head[MAX_N], current, ch[MAX_N][2], fa[MAX_N];
ll sum[MAX_N], val[MAX_N], vsum[MAX_N], ans;
struct edge
{
int to, nxt;
} edges[MAX_N << 1];
#define lson (ch[p][0])
#define rson (ch[p][1])
#define mid ((l + r) >> 1)
void addpath(int src, int dst)
{
edges[current].to = dst, edges[current].nxt = head[src];
head[src] = current++;
}
int check(int p) { return ch[fa[p]][1] == p; }
bool isRoot(int p) { return ch[fa[p]][0] != p && ch[fa[p]][1] != p; }
void pushup(int p) { sum[p] = sum[lson] + sum[rson] + val[p] + vsum[p]; }
void rotate(int x)
{
int y = fa[x], z = fa[y], dir = check(x), w = ch[x][dir ^ 1];
fa[x] = z;
if (!isRoot(y))
ch[z][check(y)] = x;
fa[y] = x, ch[x][dir ^ 1] = y;
fa[w] = y, ch[y][dir] = w;
pushup(y), pushup(x);
}
void splay(int p)
{
for (int fat = fa[p]; fat = fa[p], !isRoot(p); rotate(p))
if (!isRoot(fat))
rotate(check(p) == check(fat) ? fat : p);
pushup(p);
}
ll calc(int p, ll subtree, ll succ) { return rson ? (subtree - succ) * 2 : (val[p] * 2 > subtree ? (subtree - val[p]) * 2 : subtree - 1); }
void modify(int p, int delta)
{
splay(p);
ll subtree = sum[p] - sum[lson], succ = sum[rson];
ans -= calc(p, subtree, succ), sum[p] += delta, subtree += delta, val[p] += delta;
if (succ * 2 < subtree + 1)
vsum[p] += succ, rson = 0;
ans += calc(p, subtree, succ), pushup(p);
int pre = p;
p = fa[p];
for (; p; pre = p, p = fa[p])
{
splay(p), subtree = sum[p] - sum[lson], succ = sum[rson];
ans -= calc(p, subtree, succ), sum[p] += delta, vsum[p] += delta, subtree += delta;
if (succ * 2 < subtree + 1)
vsum[p] += succ, rson = 0, succ = 0;
if (sum[pre] * 2 > subtree)
vsum[p] -= sum[pre], rson = pre, succ = sum[pre];
ans += calc(p, subtree, succ), pushup(p);
}
}
void dfs(int u, int up)
{
sum[u] = val[u];
ll mx = val[u], p = 0;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != up)
{
fa[edges[i].to] = u, dfs(edges[i].to, u), sum[u] += sum[edges[i].to];
if (mx < sum[edges[i].to])
mx = sum[edges[i].to], p = edges[i].to;
}
ans += min(sum[u] - 1, 2LL * (sum[u] - mx));
if (sum[p] * 2 >= sum[u] + 1)
ch[u][1] = p;
vsum[u] = sum[u] - val[u] - sum[ch[u][1]];
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++)
scanf("%lld", &val[i]);
for (int i = 1, u, v; i <= n - 1; i++)
scanf("%d%d", &u, &v), addpath(u, v), addpath(v, u);
dfs(1, 0), printf("%lld\n", ans);
while (q--)
{
int u, x;
scanf("%d%d", &u, &x), modify(u, x), printf("%lld\n", ans);
}
return 0;
}
// P4338.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 4e5 + 200;
typedef long long ll;
int n, q, head[MAX_N], current, ch[MAX_N][2], fa[MAX_N];
ll sum[MAX_N], val[MAX_N], vsum[MAX_N], ans;
struct edge
{
int to, nxt;
} edges[MAX_N << 1];
#define lson (ch[p][0])
#define rson (ch[p][1])
#define mid ((l + r) >> 1)
void addpath(int src, int dst)
{
edges[current].to = dst, edges[current].nxt = head[src];
head[src] = current++;
}
int check(int p) { return ch[fa[p]][1] == p; }
bool isRoot(int p) { return ch[fa[p]][0] != p && ch[fa[p]][1] != p; }
void pushup(int p) { sum[p] = sum[lson] + sum[rson] + val[p] + vsum[p]; }
void rotate(int x)
{
int y = fa[x], z = fa[y], dir = check(x), w = ch[x][dir ^ 1];
fa[x] = z;
if (!isRoot(y))
ch[z][check(y)] = x;
fa[y] = x, ch[x][dir ^ 1] = y;
fa[w] = y, ch[y][dir] = w;
pushup(y), pushup(x);
}
void splay(int p)
{
for (int fat = fa[p]; fat = fa[p], !isRoot(p); rotate(p))
if (!isRoot(fat))
rotate(check(p) == check(fat) ? fat : p);
pushup(p);
}
ll calc(int p, ll subtree, ll succ) { return rson ? (subtree - succ) * 2 : (val[p] * 2 > subtree ? (subtree - val[p]) * 2 : subtree - 1); }
void modify(int p, int delta)
{
splay(p);
ll subtree = sum[p] - sum[lson], succ = sum[rson];
ans -= calc(p, subtree, succ), sum[p] += delta, subtree += delta, val[p] += delta;
if (succ * 2 < subtree + 1)
vsum[p] += succ, rson = 0;
ans += calc(p, subtree, succ), pushup(p);
int pre = p;
p = fa[p];
for (; p; pre = p, p = fa[p])
{
splay(p), subtree = sum[p] - sum[lson], succ = sum[rson];
ans -= calc(p, subtree, succ), sum[p] += delta, vsum[p] += delta, subtree += delta;
if (succ * 2 < subtree + 1)
vsum[p] += succ, rson = 0, succ = 0;
if (sum[pre] * 2 > subtree)
vsum[p] -= sum[pre], rson = pre, succ = sum[pre];
ans += calc(p, subtree, succ), pushup(p);
}
}
void dfs(int u, int up)
{
sum[u] = val[u];
ll mx = val[u], p = 0;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != up)
{
fa[edges[i].to] = u, dfs(edges[i].to, u), sum[u] += sum[edges[i].to];
if (mx < sum[edges[i].to])
mx = sum[edges[i].to], p = edges[i].to;
}
ans += min(sum[u] - 1, 2LL * (sum[u] - mx));
if (sum[p] * 2 >= sum[u] + 1)
ch[u][1] = p;
vsum[u] = sum[u] - val[u] - sum[ch[u][1]];
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++)
scanf("%lld", &val[i]);
for (int i = 1, u, v; i <= n - 1; i++)
scanf("%d%d", &u, &v), addpath(u, v), addpath(v, u);
dfs(1, 0), printf("%lld\n", ans);
while (q--)
{
int u, x;
scanf("%d%d", &u, &x), modify(u, x), printf("%lld\n", ans);
}
return 0;
}
// P4338.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 4e5 + 200; typedef long long ll; int n, q, head[MAX_N], current, ch[MAX_N][2], fa[MAX_N]; ll sum[MAX_N], val[MAX_N], vsum[MAX_N], ans; struct edge { int to, nxt; } edges[MAX_N << 1]; #define lson (ch[p][0]) #define rson (ch[p][1]) #define mid ((l + r) >> 1) void addpath(int src, int dst) { edges[current].to = dst, edges[current].nxt = head[src]; head[src] = current++; } int check(int p) { return ch[fa[p]][1] == p; } bool isRoot(int p) { return ch[fa[p]][0] != p && ch[fa[p]][1] != p; } void pushup(int p) { sum[p] = sum[lson] + sum[rson] + val[p] + vsum[p]; } void rotate(int x) { int y = fa[x], z = fa[y], dir = check(x), w = ch[x][dir ^ 1]; fa[x] = z; if (!isRoot(y)) ch[z][check(y)] = x; fa[y] = x, ch[x][dir ^ 1] = y; fa[w] = y, ch[y][dir] = w; pushup(y), pushup(x); } void splay(int p) { for (int fat = fa[p]; fat = fa[p], !isRoot(p); rotate(p)) if (!isRoot(fat)) rotate(check(p) == check(fat) ? fat : p); pushup(p); } ll calc(int p, ll subtree, ll succ) { return rson ? (subtree - succ) * 2 : (val[p] * 2 > subtree ? (subtree - val[p]) * 2 : subtree - 1); } void modify(int p, int delta) { splay(p); ll subtree = sum[p] - sum[lson], succ = sum[rson]; ans -= calc(p, subtree, succ), sum[p] += delta, subtree += delta, val[p] += delta; if (succ * 2 < subtree + 1) vsum[p] += succ, rson = 0; ans += calc(p, subtree, succ), pushup(p); int pre = p; p = fa[p]; for (; p; pre = p, p = fa[p]) { splay(p), subtree = sum[p] - sum[lson], succ = sum[rson]; ans -= calc(p, subtree, succ), sum[p] += delta, vsum[p] += delta, subtree += delta; if (succ * 2 < subtree + 1) vsum[p] += succ, rson = 0, succ = 0; if (sum[pre] * 2 > subtree) vsum[p] -= sum[pre], rson = pre, succ = sum[pre]; ans += calc(p, subtree, succ), pushup(p); } } void dfs(int u, int up) { sum[u] = val[u]; ll mx = val[u], p = 0; for (int i = head[u]; i != -1; i = edges[i].nxt) if (edges[i].to != up) { fa[edges[i].to] = u, dfs(edges[i].to, u), sum[u] += sum[edges[i].to]; if (mx < sum[edges[i].to]) mx = sum[edges[i].to], p = edges[i].to; } ans += min(sum[u] - 1, 2LL * (sum[u] - mx)); if (sum[p] * 2 >= sum[u] + 1) ch[u][1] = p; vsum[u] = sum[u] - val[u] - sum[ch[u][1]]; } int main() { memset(head, -1, sizeof(head)); scanf("%d%d", &n, &q); for (int i = 1; i <= n; i++) scanf("%lld", &val[i]); for (int i = 1, u, v; i <= n - 1; i++) scanf("%d%d", &u, &v), addpath(u, v), addpath(v, u); dfs(1, 0), printf("%lld\n", ans); while (q--) { int u, x; scanf("%d%d", &u, &x), modify(u, x), printf("%lld\n", ans); } return 0; }