Pjudge #21728. 【CTS Round #1 Day 1】地雷 题解

Description

给定一棵 nn 个顶点的树,其中第 ii1in11 \le i \le n-1)条边连接了顶点 uiu_iviv_i1ui,vin1 \le u_i, v_i \le n),其长度为 wiw_i

在每个顶点上均埋藏着一棵地雷。第 ii1in1 \le i \le n)个顶点上的地雷的爆炸半径为 RiR_i ,伤害值为 hih_i

我们定义 dist(i,j)\mathrm{dist}(i, j) 表示在树上顶点 ii 与顶点 jj 的最短距离。即,dist(i,j)\mathrm{dist}(i, j) 为顶点 iijj 之间唯一的简单路径的边权之和。

当顶点 ii 处的地雷爆炸时,所有距离其不超过 RiR_i 的地雷都会一起爆炸。即,对于所有满足 dist(i,j)Ri\mathrm{dist}(i, j) \le R_i 的地雷 jj,如果其还没有爆炸,那么他也会被引爆。

对于每个 iii=1,2,,ni = 1, 2, \cdots, n),你希望求出,如果在起始时引爆了顶点 ii 处的地雷,则最终所有被引爆的地雷的伤害值之和。

1n1051 \le n \le 10^51hi1091\le h_i\le 10^90Ri10180 \le R_i \le 10^{18}1ui,vin1 \le u_i, v_i \le n1wi10121 \le w_i \le 10^{12}

Solution

首先这题爆炸的过程会跳来跳去,没有一个固定的转移过程,考虑用点分治把跳的过程限定在点分树子树里。

先递归处理子树。设 frt,xf_{rt,x} 表示从 xx 炸到 rtrt 的最大剩余长度(只考虑 rtrt 的子树),炸不到就设为 -\inftygrt,xg_{rt,x} 表示要想从 rtrt 炸到 xx 的最小初始剩余长度。那么 xx 能炸到 yy 的条件是存在一个 rtrt,使得 frt,xgrt,yf_{rt,x}\geq g_{rt,y}

先求 grt,xg_{rt,x},在 rtrt 点分树子树里的每个点 kk 维护一个 vector,表示 kk 的子树没有被炸到的点,并按照其与 kk 的距离从小到大排序。

然后直接从小到大枚举初始长度,同时维护一个队列表示新炸到的点,如果队列非空就拿出队首,枚举它炸到的下一个点的 LCA,在 LCA 的 vector 里从前往后枚举还没被炸到的点更新即可。

这部分时间复杂度是 O(nlog2n)O(n\log^2n)


再求 frt,xf_{rt,x}。这里先求出从 xx 开始第一次炸到 rtrt 最大剩余长度,枚举 xx 在炸到 rtrt 之前炸到的最后一个点 yy,由于 xxyy 的 LCA 在 rtrt 的子树内,就转化为了已经求出来的 dp 值了。

具体地,设枚举的 LCA 是 kk,则如果 fk,xgk,xf_{k,x}\geq g_{k,x} 就说明可以让 disk,x+disk,ydis_{k,x}+dis_{k,y} 更新 frt,xf_{rt,x},这个可以双指针处理。

求出第一次炸到的结果后,利用新求出来的 grt,xg_{rt,x} 再双指针算即可。

这部分时间复杂度也是 O(nlog2n)O(n\log^2n)


现在求出了每个子树里的 dp 结果了,由于 xx 能炸到 yy 的条件是存在一个 rtrt,使得 frt,xgrt,yf_{rt,x}\geq g_{rt,y},这个 rtrt 不唯一,直接枚举 rtrt 会算重。

我们钦定 xxyy 在 LCA 处计算,但是它们跳到的深度最浅的地方不一定是 LCA,所以还需要修改 dp 状态。

frt,xf_{rt,x} 表示从 xx 炸到 rtrt 的最大剩余长度(可以炸到整棵树),grt,xg_{rt,x} 表示要想从 rtrt 炸到 xx 的最小初始剩余长度(可以炸到整棵树)。

对于整棵点分树的根的 dp 数组已经通过刚才的过程得到了,考虑从上往下转移。

假设已经求出当前 rtrt 的所有祖先 rtrt' 的 dp 数组。


先求新的 frt,xf_{rt,x}。枚举 xx 跳到 rt 之前的最后一个点 yy,设经过的深度最浅的 LCA rtrt'。那么在 rtrt' 的子树内 xx 能跳到 yy 的条件是 frt,xgrt,yf_{rt',x}\geq g_{rt',y},满足了这个条件即可对 frt,xf_{rt,x} 造成 rydisy,rtr_y-dis_{y,rt} 的贡献。

这个贡献可以在 rtrt' 处处理,但是由于 yyrtrt 的 LCA 不固定,所以需要对于每个可能的 LCA kk 维护出 rydisk,xr_y-dis_{k,x} 的最优值,更新 frt,xf_{rt,x} 时再枚举 rtrt 的祖先即可。

这部分时间复杂度是 O(nlog3n)O(n\log^3n)


最后求 grt,xg_{rt,x}。枚举 rtrt 跳到的第一个点 yy,设最终跳到 xx 的最浅点是 rtrt',则 yy 能对 grt,xg_{rt,x} 造成贡献的条件为 frt,ygrt,xf_{rt',y}\geq g_{rt',x},贡献为 disrt,ydis_{rt,y}

跟求 ff 的做法一样在可能的 LCA(rt,y)LCA(rt,y) 处维护 disk,ydis_{k,y} 的最优值即可。

这部分时间复杂度是 O(nlog3n)O(n\log^3n)

总的时间复杂度是:O(nlog3n)O(n\log^3n)

细节见代码以及官方题解

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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
#include <bits/stdc++.h>

#define int int64_t

using i64 = int64_t;

const int kMaxN = 1e5 + 5;

int n, rt;
int a[kMaxN], p[kMaxN], dep[kMaxN], res[kMaxN];
i64 r[kMaxN], f[kMaxN][25], g[kMaxN][25], dis[kMaxN][25];
std::vector<std::pair<int, i64>> G[kMaxN];
std::vector<int> T[kMaxN], vid[kMaxN];
std::vector<int> v1[kMaxN], v2[kMaxN];

template<class T> inline void chkmax(T &x, T y) { x = (x > y ? x : y); }
template<class T> inline void chkmin(T &x, T y) { x = (x < y ? x : y); }

namespace Build {
int rt, sz[kMaxN], mx[kMaxN];
bool del[kMaxN], vis[kMaxN];

void getsz(int u, int fa) {
sz[u] = 1, mx[u] = 0;
for (auto [v, w] : G[u]) {
if (v == fa || del[v]) continue;
getsz(v, u);
sz[u] += sz[v], mx[u] = std::max(mx[u], sz[v]);
}
}

void getrt(int u, int fa, int tot) {
mx[u] = std::max(mx[u], tot - sz[u]);
if (!rt || mx[u] < mx[rt]) rt = u;
for (auto [v, w] : G[u]) {
if (v == fa || del[v]) continue;
getrt(v, u, tot);
}
}

void dfs2(int u, int fa, int rt, i64 d) {
dis[u][dep[u] - dep[rt]] = d;
for (auto [v, w] : G[u]) {
if (v == fa || !vis[v]) continue;
dfs2(v, u, rt, d + w);
}
}

void dfs1(int u) {
del[u] = 1;
for (int i = u; i; i = p[i]) vid[i].emplace_back(u);
for (auto [v, w] : G[u]) {
if (del[v]) continue;
rt = 0, getsz(v, 0), getrt(v, 0, sz[u]);
T[u].emplace_back(rt), p[rt] = u, dep[rt] = dep[u] + 1;
dfs1(rt);
}
for (auto x : vid[u]) vis[x] = 1;
dfs2(u, 0, u, 0);
for (auto x : vid[u]) vis[x] = 0;
}

void build() {
mx[rt = 0] = 1e9, getsz(1, 0), getrt(1, 0, n), dep[rt] = 1, dfs1(::rt = rt);
for (int i = 1; i <= n; ++i)
std::sort(vid[i].begin(), vid[i].end(), [&] (int x, int y) { return dis[x][dep[x] - dep[i]] < dis[y][dep[y] - dep[i]]; });
}
} // namespace Build

namespace Part1 {

void dfs(int t) {
for (auto v : T[t]) dfs(v);
{ // get g
static bool vis[kMaxN] = {0};
static std::vector<int> vec[kMaxN];
for (auto i : vid[t]) {
vec[i] = vid[i], std::reverse(vec[i].begin(), vec[i].end());
vis[i] = 0;
}
for (auto v : vid[t]) {
std::queue<int> q;
q.emplace(v);
int val = dis[v][dep[v] - dep[t]];
for (; !q.empty();) {
int i = q.front(); q.pop();
for (int lca = i; dep[lca] >= dep[t]; lca = p[lca]) {
while (true) {
for (; vec[lca].size() && vis[vec[lca].back()]; vec[lca].pop_back()) {}
if (!vec[lca].size()) break;
int j = vec[lca].back();
assert(!vis[j]);
if (dis[i][dep[i] - dep[lca]] + dis[j][dep[j] - dep[lca]] <= r[i]) {
g[j][dep[j] - dep[t]] = val, vis[j] = 1, q.emplace(j);
} else {
break;
}
}
}
}
}
v2[t] = vid[t];
std::sort(v2[t].begin(), v2[t].end(), [&] (int i, int j) { return g[i][dep[i] - dep[t]] < g[j][dep[j] - dep[t]]; });
}
//
{ // get f
for (auto v : vid[t]) {
if (v == t) continue;
i64 mx = -1e18;
for (int i = 0, j = 0; i < v1[v].size(); ++i) {
int x = v1[v][i];
for (; j < v2[v].size(); ++j) {
int y = v2[v][j];
if (f[x][dep[x] - dep[v]] < g[y][dep[y] - dep[v]]) break;
chkmax(mx, r[y] - dis[y][dep[y] - dep[t]]);
}
if (mx >= 0) chkmax(f[x][dep[x] - dep[t]], mx);
}
}
f[t][0] = r[t];
v1[t] = vid[t];
std::sort(v1[t].begin(), v1[t].end(), [&] (int i, int j) { return f[i][dep[i] - dep[t]] < f[j][dep[j] - dep[t]]; });
i64 mx = -1e18;
for (int i = 0, j = 0; i < v1[t].size(); ++i) {
int x = v1[t][i];
for (; j < v2[t].size(); ++j) {
int y = v2[t][j];
if (f[x][dep[x] - dep[t]] < g[y][dep[y] - dep[t]]) break;
chkmax(mx, r[y] - dis[y][dep[y] - dep[t]]);
}
if (mx >= 0) chkmax(f[x][dep[x] - dep[t]], mx);
}
std::sort(v1[t].begin(), v1[t].end(), [&] (int i, int j) { return f[i][dep[i] - dep[t]] < f[j][dep[j] - dep[t]]; });
}
}

void solve() {
memset(f, 0xcf, sizeof(f));
memset(g, 0x3f, sizeof(g));
dfs(rt);
}
} // namespace Part1

namespace Part2 {
void dfs(int t) {
std::sort(v1[t].begin(), v1[t].end(), [&] (int i, int j) { return f[i][dep[i] - dep[t]] < f[j][dep[j] - dep[t]]; });
std::sort(v2[t].begin(), v2[t].end(), [&] (int i, int j) { return g[i][dep[i] - dep[t]] < g[j][dep[j] - dep[t]]; });
{ // get f
static int mx[kMaxN];
for (auto i : vid[t]) mx[i] = -2e18;
for (int i = 0, j = 0; i < v1[t].size(); ++i) {
int x = v1[t][i];
for (; j < v2[t].size(); ++j) {
int y = v2[t][j];
if (f[x][dep[x] - dep[t]] < g[y][dep[y] - dep[t]]) break;
for (int pp = y; dep[pp] >= dep[t]; pp = p[pp]) chkmax(mx[pp], r[y] - dis[y][dep[y] - dep[pp]]);
}
std::vector<int> vec;
for (int pp = x; dep[pp] > dep[t]; pp = p[pp]) {
for (int qq = pp; dep[qq] >= dep[t]; qq = p[qq])
if (mx[qq] - dis[pp][dep[pp] - dep[qq]] >= 0)
chkmax(f[x][dep[x] - dep[pp]], mx[qq] - dis[pp][dep[pp] - dep[qq]]);
}
}
}
//
{ // get g
static int mi[kMaxN];
for (auto i : vid[t]) mi[i] = 2e18;
for (int i = (int)v2[t].size() - 1, j = (int)v1[t].size() - 1; ~i; --i) {
int x = v2[t][i];
for (; ~j; --j) {
int y = v1[t][j];
if (g[x][dep[x] - dep[t]] > f[y][dep[y] - dep[t]]) break;
for (int pp = y; dep[pp] >= dep[t]; pp = p[pp]) chkmin(mi[pp], dis[y][dep[y] - dep[pp]]);
}
for (int pp = x; dep[pp] > dep[t]; pp = p[pp]) {
for (int qq = pp; dep[qq] >= dep[t]; qq = p[qq])
chkmin(g[x][dep[x] - dep[pp]], mi[qq] + dis[pp][dep[pp] - dep[qq]]);
}
}
}
//
{ // get res
int sum = 0;
for (int i = 0, j = 0; i < v1[t].size(); ++i) {
int x = v1[t][i];
for (; j < v2[t].size(); ++j) {
int y = v2[t][j];
if (f[x][dep[x] - dep[t]] < g[y][dep[y] - dep[t]]) break;
sum += a[y];
}
res[x] += sum;
}
for (auto v : T[t]) {
int sum = 0;
std::vector<int> v1 = ::v1[v], v2 = ::v2[v];
std::sort(v1.begin(), v1.end(), [&] (int i, int j) { return f[i][dep[i] - dep[t]] < f[j][dep[j] - dep[t]]; });
std::sort(v2.begin(), v2.end(), [&] (int i, int j) { return g[i][dep[i] - dep[t]] < g[j][dep[j] - dep[t]]; });
for (int i = 0, j = 0; i < v1.size(); ++i) {
int x = v1[i];
for (; j < v2.size(); ++j) {
int y = v2[j];
if (f[x][dep[x] - dep[t]] < g[y][dep[y] - dep[t]]) break;
sum += a[y];
}
res[x] -= sum;
}
}
}
for (auto v : T[t]) dfs(v);
}

void solve() {
dfs(rt);
}
} // namespace Part2

int LCA(int x, int y) {
if (dep[x] < dep[y]) std::swap(x, y);
for (; dep[x] > dep[y]; x = p[x]) {}
for (; x != y; x = p[x], y = p[y]) {}
return x;
}

bool check(int x, int y) {
int lca = LCA(x, y);
return f[x][dep[x] - dep[lca]] >= g[y][dep[y] - dep[lca]];
}

void dickdreamer() {
std::cin >> n;
for (int i = 1; i <= n; ++i) std::cin >> a[i];
// for (int i = 1; i <= n; ++i) a[i] = 1;
for (int i = 1; i <= n; ++i) std::cin >> r[i];
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);
}
Build::build(), Part1::solve(), Part2::solve();
for (int i = 1; i <= n; ++i) std::cout << res[i] << " \n"[i == 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;
}

Pjudge #21728. 【CTS Round #1 Day 1】地雷 题解
https://sobaliuziao.github.io/2025/09/28/post/72f31ef5.html
作者
Egg_laying_master
发布于
2025年9月28日
许可协议