洛谷 P2680 [NOIP2015 提高组] 运输计划
洛谷 P2680 [NOIP2015 提高组] 运输计划
题意
给出一棵树和 \(m\) 条路径,可以选择一条边,把边权改为 \(0\),求 \(m\) 条路经长度最大值的最小值。
思路
看到最大值最小,可以想到二分答案,答案具有单调性。
考虑如何判定答案 \(x\) 是否可行。
统计所有长度大于 \(x\) 的路径,统计它们共同经过的边和长度的最大值。
如果把它们共同经过的一条边权改为 \(0\) 后,长度最大值缩小到 \(x\) 以下,则答案可行。
统计共同经过的边可以使用边权树上差分,统计每条边被经过的次数。
如果某条边被经过的次数等于长度大于 \(x\) 的路径数量,则它就被共同经过。
如果没有长度大于 \(x\) 的边,答案显然可行。
直接暴力二分 \(0\) 到极大值会时间超限。
求出最大边权 \(W\) 和所有路径长度最大值 \(M\),二分区间为 \([W,M-W]\)。
因为最多把最大边权设为 \(0\)。\(W \le 1000\),可以通过。
时间复杂度:\(O((n+m)\log W)\)。
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 300005 + 5;
const int mod = 998244353;
int tot, ver[N << 1], nxt[N << 1], head[N], edge[N << 1];
int n, m, fa[N][20], de[N], dis[N], a[N];
struct path {int u, v, lca, w;};
path p[N];
void add(int x, int y, int z) {
ver[++ tot] = y;
nxt[tot] = head[x];
head[x] = tot;
edge[tot] = z;
}
void DFS(int x) {
de[x] = de[fa[x][0]] + 1;
for (int i = 1; i <= 19; i ++)
fa[x][i] = fa[fa[x][i - 1]][i - 1];
for (int i = head[x], y; i; i = nxt[i]) {
y = ver[i];
if (y == fa[x][0]) continue;
fa[y][0] = x, dis[y] = dis[x] + edge[i];
DFS(y);
}
}
inline int LCA(int x, int y) {
if (de[x] < de[y]) swap(x, y);
for (int i = 19; i >= 0; i --)
if (de[fa[x][i]] >= de[y]) x = fa[x][i];
if (x == y) return x;
for (int i = 19; i >= 0; i --)
if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
void dfs(int x) {
for (int i = head[x], y; i; i = nxt[i]) {
if ((y = ver[i]) == fa[x][0]) continue;
dfs(y);
a[x] += a[y];
}
}
bool check(int x) {
memset(a, 0, sizeof(a));
int cnt = 0, mx = 0;
for (int i = 1; i <= m; i ++) {
if (p[i].w > x) {
a[p[i].u] ++;
a[p[i].v] ++;
a[p[i].lca] -= 2;
cnt ++;
mx = max(mx, p[i].w);
}
}
if (!cnt) return 1;
dfs(1);
for (int i = 2; i <= n; i ++)
if (a[i] >= cnt && mx - dis[i] + dis[fa[i][0]] <= x) return 1;
return 0;
}
int main() {
scanf("%d%d", &n, &m);
int MAXW = 0;
for (int i = 1, u, v, w; i < n; i ++) {
scanf("%d%d%d", &u, &v, &w);
add(u, v, w); add(v, u, w);
MAXW = max(MAXW, w);
}
DFS(1);
int maxw = 0;
for (int i = 1, u, v; i <= m; i ++) {
cin >> u >> v;
p[i] = {u, v, LCA(u, v), dis[u] + dis[v] - 2 * dis[LCA(u, v)]};
maxw = max(maxw, p[i].w);
}
int l = maxw - MAXW, r = maxw, mid, res;
while (l <= r) {
mid = (l + r) >> 1;
if (check(mid)) r = mid - 1, res = mid;
else l = mid + 1;
}
printf("%d\n", res);
return 0;
}
本文来自博客园,作者:maniubi,转载请注明原文链接:https://www.cnblogs.com/maniubi/p/18387946,orz