只要你跑的够快,锅就追不上你

0%

「NOI 2018」情报中心(线段树 + 虚树)

题目描述

「NOI 2018」情报中心(LOJ 2722)

给定一棵 $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
#include <bits/stdc++.h>
2
#define mid ((l + r) >> 1)
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
#ifdef ONLINE_JUDGE
254
	freopen("center.in", "r", stdin);
255
	freopen("center.out", "w", stdout);
256
#endif
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
}