QOJ #5439. Meet in the Middle 题解

Description

Byteland 有 nn 座城市,从 11nn 编号,且有 n1n-1 条双向公路和 n1n-1 条双向铁路连接它们。

对于任意一对城市,都可以仅通过公路互相到达;同样,仅通过铁路也能互相到达。

Alice 和 Bob 计划在 Byteland 进行 qq 次长途旅行。在每次旅行中,Alice 从第 aa 座城市出发,只通过公路访问一些其他城市;Bob 从第 bb 座城市出发,只通过铁路访问一些其他城市。

两人最终会在同一座目的城市会合,并且在整个旅途中都不会重复访问任何城市。

你需要求出,在所有目的城市的选择中,他们路线总长度的最大值。

n105,q5×105n\leq 10^5,q\leq 5\times 10^5

Solution

首先对于一棵树,每次求距离某个点最远的点,有两种做法,第一种是离线下来用数据结构维护距离;第二种方法是由于最远的点一定是直径端点,所以可以求出全局直径后 O(1)O(1) 做。

这里有两棵树,用某一种方法都不太好做,考虑将这两种方法结合起来。

先把询问离线下来,对第一棵树做第一种方法,即按照 dfs 的顺序枚举询问在第一棵树的点 xx,在移动的过程中维护 wiw_i 表示 iixx 的距离。

现在问题等价于求 wi+dis2(i,y)w_i+dis_2(i,y) 的最大值,这个对第二棵树的每个点 jj,建立一个虚点 jj',与 jj 连边权值为 wjw_j 就能将问题转化为直径问题。

但是移动 xx 的过程中 wiw_i 会变化,考虑用线段树维护子树内节点的直径。

注意到线段树修改的过程是找到若干个线段树区间 [l,r][l,r] 再区间打标记,由于整体打标记不会影响直径,所以找到这些区间后向上合并直径就行了。

时间复杂度:O(nlogn+q)O(n\log n+q)

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
#include <bits/stdc++.h>

#define int int64_t

using pii = std::pair<int, int>;

const int kMaxN = 1e5 + 5, kMaxQ = 5e5 + 5;

int n, q, pos;
int res[kMaxQ];
std::vector<std::pair<int, int>> qq[kMaxN];

struct Tree {
int dfn_cnt, dfn[kMaxN], idx[kMaxN], sz[kMaxN], dis[kMaxN], st[kMaxN][18];
std::vector<std::pair<int, int>> G[kMaxN];
int get(int x, int y) { return dfn[x] < dfn[y] ? x : y; }
void add(int u, int v, int w) { G[u].emplace_back(v, w), G[v].emplace_back(u, w); }
void dfs(int u, int fa) {
st[dfn[u] = ++dfn_cnt][0] = fa, idx[dfn_cnt] = u, sz[u] = 1;
for (auto [v, w] : G[u]) {
if (v == fa) continue;
dis[v] = dis[u] + w;
dfs(v, u);
sz[u] += sz[v];
}
}
int LCA(int x, int y) {
if (x == y) return x;
if (dfn[x] > dfn[y]) std::swap(x, y);
int k = std::__lg(dfn[y] - dfn[x]);
return get(st[dfn[x] + 1][k], st[dfn[y] - (1 << k) + 1][k]);
}
int getdis(int x, int y) { return dis[x] + dis[y] - 2 * dis[LCA(x, y)]; }
void prework() {
dfn_cnt = 0, dfs(1, 0);
for (int i = 1; i <= std::__lg(dfn_cnt); ++i)
for (int j = 1; j <= dfn_cnt - (1 << i) + 1; ++j)
st[j][i] = get(st[j][i - 1], st[j + (1 << (i - 1))][i - 1]);
}
} t1, t2;

int calc(int x, int y) { return t1.getdis(pos, x) + t1.getdis(pos, y) + t2.getdis(x, y); }

pii merge(pii a, pii b) {
if (!a.first) return b;
if (!b.first) return a;
std::vector<int> vec = {a.first, a.second, b.first, b.second};
pii ret = {0, 0};
int now = -1e9;
for (int i = 0; i < 4; ++i) {
for (int j = i + 1; j < 4; ++j) {
int val = calc(vec[i], vec[j]);
if (val > now) ret = {vec[i], vec[j]}, now = val;
}
}
return ret;
}

struct SGT {
pii t[kMaxN * 4];
void pushup(int x) { t[x] = merge(t[x << 1], t[x << 1 | 1]); }
void build(int x, int l, int r) {
if (l == r) return void(t[x] = {t1.idx[l], t1.idx[l]});
int mid = (l + r) >> 1;
build(x << 1, l, mid), build(x << 1 | 1, mid + 1, r);
pushup(x);
}
void update(int x, int l, int r, int ql, int qr) {
if (l > qr || r < ql) return;
else if (l >= ql && r <= qr) return;
int mid = (l + r) >> 1;
update(x << 1, l, mid, ql, qr), update(x << 1 | 1, mid + 1, r, ql, qr);
pushup(x);
}
} sgt;

void dfs(int u, int fa) {
for (auto [b, id] : qq[u]) {
auto [x, y] = sgt.t[1];
res[id] = std::max({res[id], t1.getdis(x, u) + t2.getdis(x, b), t1.getdis(y, u) + t2.getdis(y, b)});
}
for (auto [v, w] : t1.G[u]) {
if (v == fa) continue;
pos = v, sgt.update(1, 1, n, t1.dfn[v], t1.dfn[v] + t1.sz[v] - 1);
dfs(v, u);
pos = u, sgt.update(1, 1, n, t1.dfn[v], t1.dfn[v] + t1.sz[v] - 1);
}
}

void dickdreamer() {
std::cin >> n >> q;
for (int i = 1; i < n; ++i) {
int u, v, w;
std::cin >> u >> v >> w;
t1.add(u, v, w);
}
for (int i = 1; i < n; ++i) {
int u, v, w;
std::cin >> u >> v >> w;
t2.add(u, v, w);
}
for (int i = 1; i <= q; ++i) {
int a, b;
std::cin >> a >> b;
qq[a].emplace_back(b, i);
}
t1.prework(), t2.prework();
pos = 1, sgt.build(1, 1, n);
dfs(1, 0);
for (int i = 1; i <= q; ++i) std::cout << res[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;
}

QOJ #5439. Meet in the Middle 题解
https://sobaliuziao.github.io/2025/08/14/post/9fc15d02.html
作者
Egg_laying_master
发布于
2025年8月14日
许可协议