P5773 [JSOI2016] 轻重路径 题解

Description

在二叉树上,不断删除叶子,你要维护其重链剖分后重儿子编号和。如果两个孩子大小相同,在一开始连向左儿子,后面保持修改前的连接。

n2×105n\leq 2\times 10^5

Solution

考虑把一个叶子 xx 删掉会对改变哪些点的重儿子。

首先改变的点 yy 一定在 xx 到根的链上,同时要求 szyszfay2sz_y\leq\frac{sz_{fa_y}}{2}

那么不妨设当前从上到下修改到 rtrt,则 szyszfay2szrt2sz_y\leq\frac{sz_{fa_y}}{2}\leq\frac{sz_{rt}}{2},可以通过倍增找到链上最上面的满足 szyszrt2sz_y\leq\frac{sz_{rt}}{2}yy,暴力判断 yy 能否被修改然后将 rtrt 设为 yy 继续找即可。

由于有 O(nlog2n)O(n\log^2n) 次查询子树大小的操作,直接树状数组为 O(nlog3n)O(n\log^3n),用分块 O(1)O(1) 求区间和即可做到 O(nlog2n+nn)O(n\log^2n+n\sqrt n)

时间复杂度:O(nlog3n)/O(nlog2n+nn)O(n\log^3n)/O(n\log^2n+n\sqrt n)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 2e5 + 5;

int n, m, lg;
int64_t sum;
int a[kMaxN], p[kMaxN][19], dfn[kMaxN], sz[kMaxN], wson[kMaxN];
int L[kMaxN], R[kMaxN];
std::vector<int> G[kMaxN];

struct BIT {
int c[kMaxN];

void upd(int x, int v) {
for (; x <= n; x += x & -x) c[x] += v;
}
int qry(int x) {
int ret = 0;
for (; x; x -= x & -x) ret += c[x];
return ret;
}
int qry(int l, int r) { return l <= r ? qry(r) - qry(l - 1) : 0; }
} bit;

int getsz(int x) {
if (!x) return 0;
else return bit.qry(dfn[x], dfn[x] + sz[x] - 1);
}

void dfs(int u, int fa) {
static int cnt = 0;
dfn[u] = ++cnt, sz[u] = 1, p[u][0] = fa;
for (int i = 1; i <= lg; ++i) p[u][i] = p[p[u][i - 1]][i - 1];
for (auto v : G[u]) {
if (v == fa) continue;
dfs(v, u);
sz[u] += sz[v];
if (sz[v] > sz[wson[u]]) wson[u] = v;
}
sum += wson[u];
}

void update(int x) {
bit.upd(dfn[x], -1);
int rt = 1;
for (; rt != x;) {
int y = x, now = getsz(rt);
for (int i = lg; ~i; --i)
if (p[y][i] && getsz(p[y][i]) <= getsz(rt) / 2)
y = p[y][i];
int fa = p[y][0];
if (y == wson[fa]) {
int z = L[fa] + R[fa] - y;
if (getsz(z) > getsz(y)) sum -= y, wson[fa] = z, sum += z;
}
rt = y;
}
if (x == wson[p[x][0]]) sum -= x;
}

void dickdreamer() {
std::cin >> n;
lg = std::__lg(n);
for (int i = 1; i <= n; ++i) {
std::cin >> L[i] >> R[i];
if (L[i]) G[i].emplace_back(L[i]);
if (R[i]) G[i].emplace_back(R[i]);
}
dfs(1, 0);
std::cin >> m;
for (int i = 1; i <= n; ++i) bit.upd(i, 1);
std::cout << sum << '\n';
for (int i = 1; i <= m; ++i) {
int x;
std::cin >> x;
update(x);
std::cout << sum << '\n';
}
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

P5773 [JSOI2016] 轻重路径 题解
https://sobaliuziao.github.io/2024/12/16/post/cfd00799.html
作者
Egg_laying_master
发布于
2024年12月16日
许可协议