题目大意
给定一棵 $n$ 个结点的树,第 $i$ 条边的长度为 $l_i$。还要额外地联结 $m$ 次边,第 $j$ 次对于任意 $u$ 在树上路径 $(a_j, b_j)$ 上,$v$ 在树上路径 $(c_j, d_j)$ 上,都从 $u$ 向 $v$ 连一条长度为 $e_j$ 的边。问每个点走到 $k$ 点的最短路。
数据范围:$n \le 2.5 \times 10^5, m \le 10^5$。
思路分析
I like this problem :)
显然树上的边不是复杂度瓶颈。下面我们讨论额外加的边。
暴力建边会有 $O(m \times n^2)$ 条,不可取。由于每次连的边长度相同,我们可以让所有起点向一个点连长度为 $0$ 的边,然后从这个点向另一个点连长度为 $e$ 的边,最后从那个点向所有终点连长度为 $0$ 的边。这样我们就将边数减少到了 $O(m \times n)$。
发现边的端点肯定在树上形成连续的一段,容易让我们想到线段树优化建图。不过这题将序列问题放到了树上,我们考虑树链剖分。因为两点之间在树上只会经过 $O(\log n)$ 条重链,我们考虑对于每一条重链建一棵线段树,然后找出对应的一些区间并连边。这样建出的图的边数是 $O(n \log^2 n)$(将 $n, m$ 算作同阶),再套上 Dijkstra 求最短路后的复杂度是 $O(n \log^3 n)$,不足以通过本题。
考虑树链剖分找 LCA 的过程。每次我们将所在链顶端深度较大的点移到链顶端的父亲结点,直到两个结点到达同一条链上。发现每次将某个结点上移的时候,它经过的总是一条重链的前缀。只有在最后一次,两个结点移动到同一条重链上的时候,它们对应的路径才有可能不是某条重链的前缀。于是,对于那 $O(\log n)$ 个前缀,我们使用前缀优化建边,而对于那一个区间,我们使用线段树优化建边。这样建出的图的边数是 $O(n \log n)$,再套上 Dijkstra 求最短路后的复杂度是 $O(n \log^2 n)$,可以通过本题。
值得注意的是,前缀优化建边的方法不是十分显然。我们用 “向外连一个前缀的边” 来举例。对于每一个点,我们拆出一个虚点,原点向虚点连长度为 $0$ 的边,然后每个虚点向下一个虚点连长度为 $0$ 的边。这样在前缀对应的最后一个点向外连边就等价于在原图上对于前缀上的所有点连出一条边了。
代码实现
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | using namespace std; |
7 | |
8 | typedef long long ll; |
9 | const int maxn = 2.5e5, maxm = 1e5, maxv = 5 * maxn + 2 * maxm, maxe = maxm * (1 + 18 + 18 * 4); |
10 | int n, m, k, a[maxm + 3], b[maxm + 3], c[maxm + 3], d[maxm + 3], e[maxm + 3]; |
11 | int sz[maxn + 3], fa[maxn + 3], dep[maxn + 3], son[maxn + 3]; |
12 | int tm, dfn[maxn + 3], pnt[maxn + 3], l[maxn + 3], r[maxn + 3], top[maxn + 3]; |
13 | int vir[maxn + 3], ch[maxv + 3][2], rt[maxn + 3], f[maxm + 3], g[maxm + 3]; |
14 | int cnt, tot, ter[maxe + 3], wei[maxe + 3], nxt[maxe + 3], lnk[maxv + 3]; |
15 | priority_queue<pair<ll, int>, vector<pair<ll, int> >, greater<pair<ll, int> > > H; |
16 | bool vis[maxv + 3]; |
17 | ll dist[maxv + 3]; |
18 | |
19 | void add(int u, int v, int w) { |
20 | ter[++tot] = v, wei[tot] = w; |
21 | nxt[tot] = lnk[u], lnk[u] = tot; |
22 | } |
23 | |
24 | void add(int u, int v, int w, bool flag) { |
25 | flag ? add(u, v, w) : add(v, u, w); |
26 | } |
27 | |
28 | void dfs1(int u, int pa = 0) { |
29 | sz[u] = 1; |
30 | for (int i = lnk[u], v; i; i = nxt[i]) { |
31 | if ((v = ter[i]) == pa) continue; |
32 | dep[v] = dep[u] + 1; |
33 | fa[v] = u; |
34 | dfs1(v, u); |
35 | sz[u] += sz[v]; |
36 | if (sz[v] > sz[son[u]]) { |
37 | son[u] = v; |
38 | } |
39 | } |
40 | } |
41 | |
42 | void dfs2(int u, int pa = 0) { |
43 | dfn[u] = ++tm, pnt[tm] = u; |
44 | r[top[u]] = tm; |
45 | if (u == top[u]) { |
46 | l[u] = tm; |
47 | } |
48 | if (son[u]) { |
49 | top[son[u]] = top[u]; |
50 | dfs2(son[u], u); |
51 | } |
52 | for (int i = lnk[u], v; i; i = nxt[i]) { |
53 | if ((v = ter[i]) == pa || v == son[u]) continue; |
54 | top[v] = v; |
55 | dfs2(v, u); |
56 | } |
57 | } |
58 | |
59 | void prefix(bool flag) { |
60 | for (int i = 1; i <= n; i++) { |
61 | vir[i] = ++cnt; |
62 | add(i, vir[i], 0, flag); |
63 | } |
64 | for (int i = 1; i <= n; i++) if (l[i]) { |
65 | for (int j = l[i]; j < r[i]; j++) { |
66 | add(vir[pnt[j]], vir[pnt[j + 1]], 0, flag); |
67 | } |
68 | } |
69 | } |
70 | |
71 | int build(int l, int r, bool flag) { |
72 | if (l == r) { |
73 | return pnt[l]; |
74 | } |
75 | int u = ++cnt; |
76 | ch[u][0] = build(l, mid, flag); |
77 | ch[u][1] = build(mid + 1, r, flag); |
78 | add(ch[u][0], u, 0, flag); |
79 | add(ch[u][1], u, 0, flag); |
80 | return u; |
81 | } |
82 | |
83 | void segment(bool flag) { |
84 | for (int i = 1; i <= n; i++) if (l[i]) { |
85 | rt[i] = build(l[i], r[i], flag); |
86 | } |
87 | } |
88 | |
89 | void query(int u, int l, int r, int lx, int rx, int y, bool flag) { |
90 | if (l >= lx && r <= rx) { |
91 | add(u, y, 0, flag); |
92 | return; |
93 | } |
94 | if (lx <= mid) { |
95 | query(ch[u][0], l, mid, lx, rx, y, flag); |
96 | } |
97 | if (rx > mid) { |
98 | query(ch[u][1], mid + 1, r, lx, rx, y, flag); |
99 | } |
100 | } |
101 | |
102 | int solve(int a, int b, bool flag) { |
103 | int c = ++cnt; |
104 | while (top[a] != top[b]) { |
105 | dep[top[a]] < dep[top[b]] ? swap(a, b) : void(); |
106 | add(vir[a], c, 0, flag); |
107 | a = fa[top[a]]; |
108 | } |
109 | dep[a] > dep[b] ? swap(a, b) : void(); |
110 | query(rt[top[a]], l[top[a]], r[top[a]], dfn[a], dfn[b], c, flag); |
111 | return c; |
112 | } |