NOIP2015 提高组 运输计划
LCA,倍增,树上差分
算法 1
对于 \(m=1\) 的数据。
答案就是路径和减去路径上的最大权值。一次 DFS 即可。
算法 2
对于链的情况。
一个序列问题,二分枚举 \(t\),那么我们需要将所有 \(> t\) 的线段变成 \(\leq t\) 的。我们先求出所有 \(> t\) 的线段的交集,如果这些线段没有交集,则无解。然后我们删除其中最大的那个,如果仍无法让最大的线段 \(\leq t\),则无解。
算法 3
对于 \(100\%\) 的数据。
与算法 2 实际上是相同的,我们可以在求 LCA 的过程中求出路径长度,但是如何求路径的交呢?考虑树上差分。假设一条路径 \(u \to v\),我们在 \(u \to v\) 的路径的电上都加上 \(1\),最后哪条边的边权是 \(> t\) 的线段的个数,这条边就是这些路径的交集。对于实现这个操作,我们可以在 \(u,v\) 的地方加上 \(1\),在 \(\operatorname{LCA}(u,v)\) 处减去 \(2\),这样我们回溯时更新,\(u,v\) 的路径都加上了 \(1\),从 \(\operatorname{LCA}(u,v)\) 到其父亲的边不在路径上,更新时会加上两次 \(1\),所以减去 \(2\)。
求路径长度时间复杂度为 \(O(m \log n)\),求解部分时间复杂度为 \(O(n \log w)\)。
#include <bits/stdc++.h>
const int N = 300005;
int n, m, c[N], maxt;
std::vector<std::pair<int, int>> g[N];
struct Segment {
int u, v, lca, dis;
} seg[N];
struct LCA {
int f[N][20], dis[N][20], depth[N];
void init(int u, int father) {
f[u][0] = father;
depth[u] = depth[father] + 1;
for (int i = 1; i <= std::__lg(depth[u]); i++) {
f[u][i] = f[f[u][i - 1]][i - 1];
dis[u][i] = dis[u][i - 1] + dis[f[u][i - 1]][i - 1];
}
for (auto [v, w] : g[u]) {
if (v == father) {
continue;
}
dis[v][0] = w;
init(v, u);
}
}
std::pair<int, int> operator()(int u, int v) {
int ans = 0;
if (depth[u] < depth[v]) {
std::swap(u, v);
}
while (depth[u] > depth[v]) {
int jump = std::__lg(depth[u] - depth[v]);
ans += dis[u][jump];
u = f[u][jump];
}
if (u == v) {
return {u, ans};
}
for (int i = std::__lg(depth[u]); i >= 0; i--) {
if (f[u][i] != f[v][i]) {
ans += dis[u][i] + dis[v][i];
u = f[u][i], v = f[v][i];
}
}
ans += dis[u][0] + dis[v][0];
return {f[u][0], ans};
}
} lca;
void dfs(int u, int father) {
for (auto [v, w] : g[u]) {
if (v == father) {
continue;
}
dfs(v, u);
c[u] += c[v];
}
}
bool check(int t) {
memset(c, 0, sizeof(c));
int cnt = 0, res = 0;
for (int i = 1; i <= m; i++) {
if (seg[i].dis <= t) {
continue;
}
c[seg[i].u]++;
c[seg[i].v]++;
c[seg[i].lca] -= 2;
cnt++;
}
dfs(1, 0);
for (int i = 1; i <= n; i++) {
if (c[i] == cnt) {
res = std::max(res, lca.dis[i][0]);
}
}
return res >= maxt - t;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> m;
int l = 0, r = 0;
for (int i = 1; i < n; i++) {
int u, v, w;
std::cin >> u >> v >> w;
g[u].push_back({v, w});
g[v].push_back({u, w});
l = std::max(l, w);
}
lca.init(1, 0);
for (int i = 1; i <= m; i++) {
int u, v;
std::cin >> u >> v;
int a, b;
std::tie(a, b) = lca(u, v);
seg[i] = {u, v, a, b};
r = std::max(r, b);
}
l = r - l;
maxt = r;
int ans = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
std::cout << ans << '\n';
return 0;
}

浙公网安备 33010602011771号