CF516D Drazil and Morning Exercise 题解

Description

  • 给定一棵 nn 个点的树,边有边权。
  • 定义 fx=maxi=1ndist(x,i)f_x = \max_{i=1}^n \text{dist}(x,i)
  • qq 次询问最大的满足 maxxsfxminxsfxl\max_{x \in s} f_x - \min_{x \in s} f_x \le l 的连通块 ss 包含的点数。
  • n105n \le 10^5q50q \le 50

Solution

这里 fuf_u 显然可以用换根 dp 求出。

但是直接做很难搞,考虑去思考 fuf_u 的性质。

有一个结论是 fuf_u 一定是 uu 到直径两端点距离中的一个,证明就考虑反证即可。

有了这个性质,如果把树的中心当作根,那么每个点的 ff 一定不小于它父亲的 ff,所以直接枚举连通块的点,那么满足条件的连通块的根一定是条链,搞个树上差分就行了。

时间复杂度:O(nqlogn)O(nq\log 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
#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 1e5 + 5;

int n, q, l, rt;
int f[kMaxN], g[kMaxN], h[kMaxN][2], d[kMaxN], p[kMaxN];
std::vector<std::pair<int, int>> G[kMaxN];
std::vector<int> vec;

void dfs1(int u, int fa) {
for (auto [v, w] : G[u]) {
if (v == fa) continue;
dfs1(v, u);
int val = h[v][0] + w;
if (val >= h[u][0]) h[u][1] = h[u][0], h[u][0] = val;
else if (val >= h[u][1]) h[u][1] = val;
}
}

void dfs2(int u, int fa) {
f[u] = std::max(g[u], h[u][0]);
for (auto [v, w] : G[u]) {
if (v == fa) continue;
g[v] = std::max(g[u], h[u][h[v][0] + w == h[u][0]]) + w;
dfs2(v, u);
}
}

void dfs3(int u, int fa) {
p[u] = fa;
vec.emplace_back(u);
int L = -1, R = vec.size(), res = vec.size() - 1;
while (L + 1 < R) {
int mid = (L + R) >> 1;
if (f[vec[mid]] >= f[u] - l) R = res = mid;
else L = mid;
}
--d[p[vec[res]]], ++d[u];
for (auto [v, w] : G[u]) {
if (v == fa) continue;
dfs3(v, u);
}
vec.pop_back();
}

void dfs4(int u, int fa) {
for (auto [v, w] : G[u]) {
if (v == fa) continue;
dfs4(v, u);
d[u] += d[v];
}
}

void dickdreamer() {
std::cin >> n;
for (int i = 1; i < n; ++i) {
int u, v, w;
std::cin >> u >> v >> w;
G[u].emplace_back(v, w), G[v].emplace_back(u, w);
}
dfs1(1, 0), dfs2(1, 0);
rt = std::min_element(f + 1, f + 1 + n) - f;
std::cin >> q;
for (int cs = 1; cs <= q; ++cs) {
std::cin >> l;
std::fill_n(d + 1, n, 0);
dfs3(rt, 0), dfs4(rt, 0);
std::cout << *std::max_element(d + 1, d + 1 + n) << '\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;
}

CF516D Drazil and Morning Exercise 题解
https://sobaliuziao.github.io/2024/02/08/post/3056a006.html
作者
Egg_laying_master
发布于
2024年2月8日
许可协议