边分治神题。

前置知识:边分治,虚树。

题意

给定 33 棵有边权的树,每棵树都含有 nn 个节点,令 disi(x,y)dis_i(x, y) 表示 (x,y)(x, y) 在第 ii 棵树上的距离。求一组 (i,j)(i, j),使得 k=13disk(i,j)\sum\limits_{k = 1}^3 dis_k(i, j) 最大,为了方便,只需输出最大值。

题解

考虑边分治。下面用 TiT_i 来表示第 ii 棵树。

T1T_1 进行边分治,设 d1(x)d_1(x) 表示 xx 点到分治边一侧的距离,那么答案就转化成了 d1(i)+d1(j)+dis2(i,j)+dis3(i,j)d_1(i) + d_1(j) + dis_2(i, j) + dis_3(i, j)。然后将当前分治块内的所有点在 T2T_2 上拉出来,建一棵虚树 T2T'_2,再设 d2(x)d_2(x) 表示 xxT2T_2 上的带权深度,答案又转化成了 d1(i)+d1(j)+d2(i)+d2(j)+dis3(i,j)2d2pd_1(i) + d_1(j) + d_2(i) + d_2(j) + dis_3(i, j) - 2d_2{p},其中 pp 表示 T2T'_2(i,j)(i, j)LCA\text{LCA},显然 pp 也是 T2T_2(i,j)(i, j)LCA\text{LCA}

所以现在枚举 pp,然后在 ppT2T'_2 的子树中,找到 (i,j)(i, j) 使:1. i,ji, jT1T_1 的分治边的两侧。2. 答案最大。接着可以发现,上面的答案可以变成 (d1+d2)(i)+(d1+d2)(j)+dis3(i,j)2d2(p)(d_1 + d_2)(i) + (d_1 + d_2)(j) + dis_3(i, j) - 2d_2(p),因为 pp 是我们枚举的,故可以省略。接着,考虑把 (d1+d2)(i)+(d1+d2)(j)(d_1 + d_2)(i) + (d_1 + d_2)(j) 给转化一下。一个精妙的处理方法是:在 T3T_3 中新建 i,ji', j',然后将 ii'ii 之间连一条边权为 d1(i)+d2(i)d_1(i) + d_2(i) 的边,对 jjjj' 同理。这样答案就转化成了 dis3(i,j)2d2(p)dis_3(i', j') - 2d_2(p),也就是求 T3T_3 中的最长路(要保证 i,ji, j 在分治边的两侧)。

我们发现,最长路竟然是可以合并的!也就是说:设 xx 只有两个儿子,l,rl, rxx 的左儿子子树的最长路两端点,l,rl', r' 是右儿子的。那么 xx 的最长路两端点一定是 l,l,r,rl, l', r, r' 中的两个!根据这个结论,就可以在 T2T'_2 上面进行合并了。有一个细节,设 LL 表示分治边左侧的点集,RR 表示分治边右侧的点集,如果要保证 (i,j)(i, j) 在分治边的两侧,那么先把 LL 的最长路和 RR 的最长路分别求出,然后再对两个最长路,分别选出一个点进行合并,这才能保证 i,ji, j 在不同侧的限制。

于是这题就做完了,时间复杂度 O(nlog2n)O(n \log^2 n)


代码有点难写,调了 1.5h。。。感叹代码能力的弱小。代码最下面的“类人群星闪耀时”是我的弱智sb错误。

代码:

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
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int N = 2E5 + 5;
int n; i64 d[3][N], sd[N], ans; bool L[N], R[N];
vector <pair <int, i64>> G[4][N];
struct t3 {
int yf[N][20], S = 19, dep[N]; i64 dd[N], bq[N];
void dfs(int x, int fa) {
for (auto [v, w] : G[3][x]) {
if (v == fa) continue;
dd[v] = dd[x] + w; yf[v][0] = x;
dep[v] = dep[x] + 1;
for (int i = 1; i <= S; ++i) yf[v][i] = yf[yf[v][i - 1]][i - 1];
dfs(v, x);
}
}
int glca(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
for (int i = S; i >= 0; --i) if (dep[u] - (1 << i) >= dep[v])
u = yf[u][i];
if (u == v) return u;
for (int i = S; i >= 0; --i) if (yf[u][i] != yf[v][i])
u = yf[u][i], v = yf[v][i];
return yf[u][0];
}
void nd(int x, i64 val) {bq[x] = val;}
i64 dis(int u, int v) {
if (!u || !v) return -1E9;
if (u == v) return 0;
return dd[u] + dd[v] - 2 * dd[glca(u, v)] + bq[u] + bq[v];
}
} pd;
struct virt {
int h[N], m, len, a[N << 1], ll, dfn[N], S = 19, yf[N][20], dep[N], ccnt;
void push(int x) {h[++len] = x;} vector <int> E[N];
void conn(int x, int y) {E[x].emplace_back(y); E[y].emplace_back(x);}
void dfs(int x, int fa) {
dfn[x] = ++ccnt;
for (auto [v, w] : G[2][x]) {
if (v == fa) continue;
d[2][v] = d[2][x] + w;
dep[v] = dep[x] + 1; yf[v][0] = x;
for (int i = 1; i <= S; ++i) yf[v][i] = yf[yf[v][i - 1]][i - 1];
dfs(v, x);
}
}
int glca(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
for (int i = S; ~i; --i) if (dep[u] - (1 << i) >= dep[v])
u = yf[u][i];
if (u == v) return u;
for (int i = S; ~i; --i)
if (yf[u][i] != yf[v][i]) {
u = yf[u][i], v = yf[v][i];
}
return yf[u][0];
}
void build() {
sort(h + 1, h + len + 1, [&](int x, int y) {return dfn[x] < dfn[y];});
for (int i = 1; i <= len; ++i) a[++ll] = h[i];
for (int i = 1; i < len; ++i) {
a[++ll] = glca(h[i], h[i + 1]);
} a[++ll] = 1;
sort(a + 1, a + 1 + ll, [&](int x, int y) {return dfn[x] < dfn[y];});
ll = unique(a + 1, a + 1 + ll) - a - 1;
for (int i = 1; i < ll; ++i) {
int lc = glca(a[i], a[i + 1]);
conn(lc, a[i + 1]);
}
}
void init() {
dep[1] = 1; for (int i = 0; i <= S; ++i) yf[1][i] = 1;
dfs(1, 0);
pd.dep[1] = 1; for (int i = 0; i <= S; ++i)
pd.yf[1][i] = 1;
pd.dfs(1, 0);
}
struct cl {
int p, q; i64 Di;
i64 val() {return Di;}
bool operator < (const cl &w) const {return Di < w.Di;}
cl (int x, int y) {p = x; q = y; Di = pd.dis(p, q);}
cl () {p = q = 0; Di = -1E9; }
} ;
cl merge(cl a, cl b) {
return max({cl(a.p, a.q), cl(a.p, b.q), cl(a.p, b.p), cl(a.q, b.p), cl(a.q, b.q), cl(b.p, b.q)});
}
cl lmg(cl a, cl b) {
return max({cl(a.p, b.p), cl(a.p, b.q), cl(a.q, b.p), cl(a.q, b.q)});
}
pair <cl, cl> dp(int x, int fa) {
cl l, r;
if (L[x]) l = cl(x, x);
else if (R[x]) r = cl(x, x);
for (auto v : E[x]) {
if (v == fa) continue;
auto [tl, tr] = dp(v, x);
ans = max(ans, lmg(l, tr).val() - 2 * d[2][x]);
ans = max(ans, lmg(r, tl).val() - 2 * d[2][x]);
l = merge(l, tl);
r = merge(r, tr);
}
return make_pair(l, r);
}
void solve() {
build();
for (int i = 1; i <= len; ++i) if (L[h[i]] || R[h[i]])
pd.nd(h[i], d[1][h[i]] + d[2][h[i]]);
dp(1, 0);
}
void clear() {
for (int i = 1; i <= ll; ++i)
E[a[i]].clear();
len = ll = 0;
}
} vi;
struct bfz {
int sz[N], root, dmx, dsum, head[N], nex[N << 1], tot = 1, m; bool vis[N];
struct E {int to, nxt; i64 dis;} edge[N << 1];
void add(int x, int y, i64 z) {
edge[++tot] = (E) {x, y, z};
nex[tot] = head[x]; head[x] = tot;
}
void getroot(int x, int fa) {
sz[x] = 1;
for (int i = head[x]; i; i = nex[i]) {
int v = edge[i].nxt;
if (v == fa || vis[i >> 1]) continue;
getroot(v, x); sz[x] += sz[v];
int tmp = max(sz[v], dsum - sz[v]);
if (dmx > tmp) {
dmx = tmp;
root = i;
}
}
}
void rebuild(int x, int fa) {
int tmp = 0, len = G[1][x].size(), last;
for (auto [v, w] : G[1][x]) {
if (v == fa) continue;
++tmp; if (tmp == 1) {
add(v, x, w); add(x, v, w);
last = x;
} else if (tmp == len - (x != 1)) {
add(last, v, w); add(v, last, w);
} else {
++m;
add(last, m, 0); add(m, last, 0);
last = m;
add(m, v, w); add(v, m, w);
}
}
for (auto [v, w] : G[1][x]) if (v != fa)
rebuild(v, x);
}
void dfs(int x, int fa, bool typ) {
if (x <= n) vi.push(x);
if (!typ) L[x] = 1;
else R[x] = 1;
for (int i = head[x]; i; i = nex[i]) {
int v = edge[i].nxt; i64 w = edge[i].dis;
if (v == fa || vis[i >> 1]) continue;
d[1][v] = d[1][x] + w;
dfs(v, x, typ);
}
}
void clear(int x, int fa) {
L[x] = R[x] = 0;
for (int i = head[x]; i; i = nex[i]) {
int v = edge[i].nxt;
if (v == fa || vis[i >> 1]) continue;
clear(v, x);
}
}
int cnt = 0;
void solve(int x, int nsum) {
root = 0; dmx = 1E9; dsum = nsum;
getroot(x, 0); if (!root) return ;
vis[root >> 1] = 1;
int u = edge[root].to, v = edge[root].nxt;
i64 w = edge[root].dis;
d[1][u] = 0; dfs(u, v, 0); d[1][v] = w; dfs(v, u, 1);
vi.solve(); vi.clear();
clear(u, v); clear(v, u);
solve(u, dsum - sz[v]);
solve(v, sz[v]);
}
void solve() {
m = n;
rebuild(1, 0);
solve(1, m);
}
} b;
signed main(void) {
ios :: sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n;
for (int i = 1; i <= 3; ++i) {
for (int j = 1; j < n; ++j) {
int u, v; i64 w; cin >> u >> v >> w;
G[i][u].emplace_back(v, w);
G[i][v].emplace_back(u, w);
}
} vi.init();
b.solve();
cout << ans << '\n';
return 0;
}
/*
类人群星闪耀时:
1. E 未清空。
2. 虚树中加入了三度化的虚点。
3. 更新答案时,没注意 a∈L,b∈R 的条件。
4. d[1][x] 忘记清空。
5. 十年OI一场空,______________。
*/