题目描述
给定一棵 $n$ 个点的树,第 $i$ 条边有权值 $c_i$。有 $m$ 条路径,第 $i$ 条路径有权值 $v_i$。求从 $m$ 条路径中选出两条至少有一条边相交的路径,得到的 $链并上的边权和 − 两条链的总费用$ 的最大值。
数据范围:$\sum{n} \le 10^6, \sum {m} \le 2 \times 10^6, 0 \le c_i \le 10^9, 0 \le v_i \le 10^{10} \times n$。
提示:分两条路径的 LCA 相同或不同讨论。
思路分析
第一部分
发现如果两条路径的 LCA 不同,那么它们的交一定是直上直下的一段。
如上图所示,我们考虑红色和蓝色两条路径。这两条路径形成的权值为:$两条链的总长度 - 两条链的总费用 - (红点到根的距离 - 绿点到根的距离)$。
我们考虑枚举红点,并同时维护经过红点的所有路径。具体地,我们对每个点 $x$ 维护 $f(x, y)$ 表示所有经过点 $x$ 的、LCA 为 $y$ 的路径中,$长度 - 费用$ 的最大值。这样,我们求出 $f(x)$ 后,就可以用 $\max_{i, j}\lbrace f(x, i) + f(x, j) + \max\lbrace d(i), d(j)\rbrace \rbrace - d(x)$ 更新答案,其中 $d(x)$ 表示点 $x$ 到根的距离。当然,我们发现答案要求的是最大值,所以上式就可以简化为:$\max_{i, j}\lbrace f(x, i) + f(x, j) + d(i)\rbrace - d(x)$。
我们考虑使用下标为结点深度的线段树来维护这样一个过程。在遍历到某个点时,我们要加入所有以它为端点的路径;在 DFS 完某个点儿子时,我们要将儿子的 $f$ 数组合并到自己身上;在某个点从 DFS 栈中出去时,我们要删除所有 LCA 恰好为它的路径。也就是说,我们只需要支持单点更新,单点清除,以及合并。在修改 / 合并过程进行的同时,我们要进行答案的更新。注意红点的下方必须分叉,所以加入某个子树之前需要先更新答案。对于修改,我们用新加入的链和原来线段树中的点更新答案;在合并两棵树 $x, y$ 的时候,我们在合并之前用 $(\text{ls}(x), \text{rs}(y))$ 以及 $(\text{rs}(x), \text{ls}(y))$ 来更新答案即可。具体实现见代码。
第二部分
如果两条路径的 LCA 相同,那么它们的交一定过 LCA。
如上图,这两条路径的权值为 $\frac{两条链的总长度 + 蓝点距离 + 绿点距离 - 两条链的总费用 \times 2}{2}$。
我们首先假设所有链的 LCA 都是根结点。我们还是考虑枚举红点。对于某个蓝点,我们给它对应的绿点赋值为 $链长 - 费用 + 蓝点深度$。这样,对于某个红点下面的两个在不同子树内部的蓝点就可以用 $\frac{绿点权值和 + 绿点距离 - 红点深度 \times 2}{2}$ 来更新答案了。
这就相当于对于每个红点子树内的蓝点求绿点的最远点对。有一个性质是如果边权为正,那么两个集合的最远点对的端点一定是某个集合中最远点对的端点。于是我们合并最远点对即可。注意到红点下面还是需要分叉,处理的方法参考第一部分。
那么如果我们有多个 LCA 该怎么办呢?对每个 LCA 建虚树再跑上面的算法即可。
这样问题就得到了全面的解决,时间复杂度 $O(n \log n)$。
这道题目,无疑是善良的出题人无私的馈赠。不仅巧妙考察了选手二合一的思维能力,还对选手的手指速度有着极高的要求。无论是 NOI 测试还是平时练习,这都是一道一个顶俩的题目。出题人相信,这道美妙的题目,可以给拼搏于逐梦之路上的你,提供一个有力的援助。
代码实现
1 |
|
2 |
|
3 | using namespace std; |
4 | |
5 | typedef long long ll; |
6 | const int maxn = 1e5, logn = 16; |
7 | const ll inf = 1e18, lim = 1e17; |
8 | int T, n, m, tot, dfn[maxn + 3], mn[logn + 3][maxn + 3], l_2[maxn + 3]; |
9 | int num[maxn + 3], dep[maxn + 3], a[maxn + 3], b[maxn + 3], c[maxn + 3]; |
10 | ll d_v[maxn + 3], val[maxn + 3], dist[maxn + 3]; |
11 | vector<int> G[maxn + 3]; |
12 | |
13 | void dfs(int u) { |
14 | dfn[u] = ++tot, mn[0][tot] = u; |
15 | for (int i = 0, v; i < G[u].size(); i++) { |
16 | v = G[u][i]; |
17 | dep[v] = dep[u] + 1; |
18 | d_v[v] = d_v[u] + num[v]; |
19 | dfs(v); |
20 | mn[0][++tot] = u; |
21 | } |
22 | } |
23 | |
24 | void prework() { |
25 | for (int i = 2; i <= tot; i++) { |
26 | l_2[i] = l_2[i >> 1] + 1; |
27 | } |
28 | for (int k = 1; 1 << k <= tot; k++) { |
29 | for (int i = 1, j = (1 << (k - 1)) + 1; i <= tot - (1 << k) + 1; i++, j++) { |
30 | mn[k][i] = min(mn[k - 1][i], mn[k - 1][j]); |
31 | } |
32 | } |
33 | } |
34 | |
35 | int lca(int x, int y) { |
36 | x = dfn[x], y = dfn[y]; |
37 | if (x > y) swap(x, y); |
38 | int t = l_2[y - x + 1]; |
39 | return min(mn[t][x], mn[t][y - (1 << t) + 1]); |
40 | } |
41 | |
42 | namespace sub_1 { |
43 | const int maxm = 4e6; |
44 | int tot, rt[maxn + 3]; |
45 | ll ans, res; |
46 | vector<int> S[maxn + 3]; |
47 | |
48 | struct node { |
49 | int ls, rs; |
50 | ll f, g; |
51 | } tr[maxm + 3]; |
52 | |
53 | void maintain(int x) { |
54 | tr[x].f = max(tr[tr[x].ls].f, tr[tr[x].rs].f); |
55 | tr[x].g = max(tr[tr[x].ls].g, tr[tr[x].rs].g); |
56 | } |
57 | |
58 | void modify(int &x, int l, int r, int y, ll f, ll g, bool type) { |
59 | if (!x) { |
60 | x = ++tot, tr[x].ls = tr[x].rs = 0; |
61 | tr[x].f = tr[x].g = -inf; |
62 | } |
63 | if (l == r) { |
64 | if (type) { |
65 | tr[x].f = f, tr[x].g = g; |
66 | } else { |
67 | tr[x].f = max(tr[x].f, f); |
68 | tr[x].g = max(tr[x].g, g); |
69 | } |
70 | return; |
71 | } |
72 | if (y <= mid) { |
73 | res = max(res, f + tr[tr[x].rs].g); |
74 | modify(tr[x].ls, l, mid, y, f, g, type); |
75 | } else { |
76 | res = max(res, tr[tr[x].ls].f + g); |
77 | modify(tr[x].rs, mid + 1, r, y, f, g, type); |
78 | } |
79 | maintain(x); |
80 | } |
81 | |
82 | int merge(int x, int y) { |
83 | if (!x || !y) { |
84 | return x | y; |
85 | } |
86 | res = max(res, tr[tr[x].ls].f + tr[tr[y].rs].g); |
87 | res = max(res, tr[tr[y].ls].f + tr[tr[x].rs].g); |
88 | tr[x].ls = merge(tr[x].ls, tr[y].ls); |
89 | tr[x].rs = merge(tr[x].rs, tr[y].rs); |
90 | tr[x].f = max(tr[x].f, tr[y].f); |
91 | tr[x].g = max(tr[x].g, tr[y].g); |
92 | return x; |
93 | } |
94 | |
95 | void dfs(int u) { |
96 | for (int i = 0; i < S[u].size(); i++) { |
97 | int x = S[u][i]; |
98 | if (u == c[x]) continue; |
99 | res = -inf; |
100 | modify(rt[u], 0, n - 1, dep[c[x]], dist[x] - val[x], dist[x] - val[x] + d_v[c[x]], false); |
101 | ans = max(ans, res - d_v[u]); |
102 | } |
103 | for (int i = 0, v; i < G[u].size(); i++) { |
104 | v = G[u][i]; |
105 | dfs(v); |
106 | modify(rt[v], 0, n - 1, dep[u], -inf, -inf, true); |
107 | res = -inf; |
108 | rt[u] = merge(rt[u], rt[v]); |
109 | ans = max(ans, res - d_v[u]); |
110 | } |
111 | } |
112 | |
113 | ll main() { |
114 | for (int i = 1; i <= n; i++) { |
115 | vector<int>().swap(S[i]); |
116 | rt[i] = 0; |
117 | } |
118 | ans = -inf; |
119 | for (int i = 1; i <= m; i++) { |
120 | S[a[i]].push_back(i); |
121 | S[b[i]].push_back(i); |
122 | } |
123 | tot = 0; |
124 | tr[0].f = -inf, tr[0].g = -inf; |
125 | dfs(1); |
126 | return ans; |
127 | } |
128 | } |
129 | |
130 | namespace sub_2 { |
131 | int tot, k, top, st[maxn + 3], vis[maxn + 3]; |
132 | ll num[maxn + 3], ans; |
133 | vector<int> S[maxn + 3], T[maxn + 3], V[maxn + 3]; |
134 | pair<int, int> pr[maxn * 2 + 3]; |
135 | |
136 | struct point { |
137 | int x; ll w; |
138 | point(int x = 0, ll w = 0): x(x), w(w) {} |
139 | }; |
140 | |
141 | struct diameter { |
142 | point a, b; ll w; |
143 | diameter(point a = point(), point b = point(), ll w = -inf): a(a), b(b), w(w) {} |
144 | friend bool operator < (const diameter &x, const diameter &y) { |
145 | return x.w < y.w || (x.w == y.w && !x.a.x); |
146 | } |
147 | } f[maxn + 3]; |
148 | |
149 | void clear(int x) { |
150 | if (vis[x] != tot) { |
151 | vis[x] = tot; |
152 | vector<int>().swap(T[x]); |
153 | vector<int>().swap(V[x]); |
154 | } |
155 | } |
156 | |
157 | void add(int u, int v) { |
158 | clear(u), clear(v); |
159 | T[u].push_back(v); |
160 | } |
161 | |
162 | ll get_dist(int x, int y) { |
163 | return d_v[x] + d_v[y] - d_v[lca(x, y)] * 2; |
164 | } |
165 | |
166 | diameter func(point a, point b, ll x, bool type) { |
167 | if (!a.x || !b.x) return point(!a.x ? b : a); |
168 | diameter ret = diameter(a, b, (a.w + b.w + get_dist(a.x, b.x)) / 2); |
169 | if (type) ans = max(ans, ret.w - x); |
170 | return ret; |
171 | } |
172 | |
173 | diameter merge(diameter x, diameter y, ll z) { |
174 | diameter ret; |
175 | ret = max(ret, func(x.a, y.a, z, true)); |
176 | ret = max(ret, func(x.a, y.b, z, true)); |
177 | ret = max(ret, func(x.b, y.a, z, true)); |
178 | ret = max(ret, func(x.b, y.b, z, true)); |
179 | ret = max(ret, max(x, y)); |
180 | return ret; |
181 | } |
182 | |
183 | void dfs(int u) { |
184 | f[u] = diameter(); |
185 | for (int i = 0, x, y; i < V[u].size(); i++) { |
186 | x = V[u][i], y = u ^ a[x] ^ b[x]; |
187 | point p = point(y, num[x] + d_v[u]); |
188 | f[u] = merge(f[u], func(p, p, 0, false), d_v[u]); |
189 | } |
190 | for (int i = 0, v; i < T[u].size(); i++) { |
191 | v = T[u][i], dfs(v); |
192 | f[u] = merge(f[u], f[v], d_v[u]); |
193 | } |
194 | } |
195 | |
196 | ll main() { |
197 | for (int i = 1; i <= n; i++) { |
198 | vector<int>().swap(S[i]); |
199 | vis[i] = 0; |
200 | } |
201 | for (int i = 1; i <= m; i++) { |
202 | S[c[i]].push_back(i); |
203 | } |
204 | tot = 0, ans = -inf; |
205 | for (int i = 1; i <= n; i++) { |
206 | k = 0; |
207 | for (int j = 0; j < S[i].size(); j++) { |
208 | int x = S[i][j]; |
209 | pr[++k] = make_pair(dfn[a[x]], a[x]); |
210 | pr[++k] = make_pair(dfn[b[x]], b[x]); |
211 | } |
212 | sort(pr + 1, pr + k + 1); |
213 | top = 0, st[++top] = i; |
214 | ++tot, clear(i); |
215 | for (int j = 1, x, y; j <= k; j++) { |
216 | x = pr[j].second; |
217 | if (x == st[top]) continue; |
218 | y = lca(x, st[top]); |
219 | if (y != st[top]) { |
220 | while (top > 1 && dfn[y] < dfn[st[top - 1]]) { |
221 | add(st[top - 1], st[top]), top--; |
222 | } |
223 | if (dfn[y] > dfn[st[top - 1]]) { |
224 | add(y, st[top]); |
225 | st[top] = y; |
226 | } else { |
227 | add(y, st[top]); |
228 | top--; |
229 | } |
230 | } |
231 | st[++top] = x; |
232 | } |
233 | for (int j = 1; j < top; j++) { |
234 | add(st[j], st[j + 1]); |
235 | } |
236 | for (int j = 0; j < S[i].size(); j++) { |
237 | int x = S[i][j]; |
238 | V[a[x]].push_back(x), V[b[x]].push_back(x); |
239 | num[x] = dist[x] - val[x] * 2; |
240 | } |
241 | for (int j = 0, v; j < T[i].size(); j++) { |
242 | v = T[i][j], dfs(v); |
243 | } |
244 | for (int j = 0; j < S[i].size(); j++) { |
245 | int x = S[i][j]; |
246 | } |
247 | } |
248 | return ans; |
249 | } |
250 | } |
251 | |
252 | void settings() { |
253 |
|
254 | freopen("center.in", "r", stdin); |
255 | freopen("center.out", "w", stdout); |
256 |
|
257 | } |
258 | |
259 | int main() { |
260 | settings(); |
261 | scanf("%d", &T); |
262 | while (T --> 0) { |
263 | scanf("%d", &n); |
264 | tot = 0; |
265 | for (int i = 1; i <= n; i++) { |
266 | vector<int>().swap(G[i]); |
267 | } |
268 | for (int i = 1, u, v, w; i < n; i++) { |
269 | scanf("%d %d %d", &u, &v, &w); |
270 | G[u].push_back(v), num[v] = w; |
271 | } |
272 | dfs(1); |
273 | prework(); |
274 | scanf("%d", &m); |
275 | for (int i = 1; i <= m; i++) { |
276 | scanf("%d %d %lld", &a[i], &b[i], &val[i]); |
277 | c[i] = lca(a[i], b[i]); |
278 | dist[i] = d_v[a[i]] + d_v[b[i]] - d_v[c[i]] * 2; |
279 | } |
280 | ll ans = max(sub_1::main(), sub_2::main()); |
281 | if (ans < -lim) { |
282 | puts("F"); |
283 | } else { |
284 | printf("%lld\n", ans); |
285 | } |
286 | } |
287 | return 0; |
288 | } |