[NOIP2015 提高组] 运输计划(二分 + lca + 树上差分)

link

求操作后的所有路径中边权和最长的边的最小值,二分

考虑 对答案的最短时间二分,每次得到一个二分值 mid

显然,我们主要关心 路径和比 mid 大的运输路径,这里要维护树上两点间的路径,所以要用到 lca,同时可以记录每个点的距离(1 -> x)dist[x],这样可以用边前缀和直接得到路径 x -> y 和:

res=dist[x]+dist[y]2dist[ lca(x,y) ]

操作只能做一次,贪心地考虑肯定是要删掉 任意一个经过所有 res>mid 的运输路径的边的集合中边权 max{resimid} 的边

也就是个判定性的问题

  • 一定要经过所有运输路径,如果删去的不是经过所有的,则肯定还有运输路径的时间要 > mid

  • 删去边的边权大于等于最大差值即可,剩下的显然都会被覆盖,也就是不会超过 mid

怎么知道这个边经过了几条路径呢?

那么要对路径上所有的边 +1,记录覆盖,很容易想到用 树上差分 维护即可。

时间复杂度为 O(nlog(nti))


做这题还有一个小心得(也不算吧,

在实现树上差分的还原时,可以选择再 dfs 一遍,也可以在 lca 初始化树的时候开一个数组记录搜索序,后边倒序循环一遍求前缀和。

我觉得以后还是用 后者更好,因为我老是把两次的 dfs 函数名打混,时不时就调 n 年 ...

#include <bits/stdc++.h>
#define re register int
#define max(x, y) (x > y ? x : y)
using namespace std;
const int N = 3e5 + 10, logN = 50;
struct edge
{
int to, w, next;
}e[N << 1];
struct road
{
int x, y, p;
}a[N];
int top, h[N];
int dep[N], dist[N], f[N][logN], d[N];
int n, m;
int seq[N], idx;
inline void add(int x, int y, int w)
{
e[++ top] = (edge){y, w, h[x]};
h[x] = top;
}
void dfs(int u, int fa)
{
dep[u] = dep[fa] + 1;
seq[++ idx] = u;
f[u][0] = fa;
for (re i = 1; i <= log2(n); i ++)
f[u][i] = f[f[u][i - 1]][i - 1];
for (re i = h[u]; i; i = e[i].next)
{
int v = e[i].to, w = e[i].w;
if (v == fa) continue;
dist[v] = dist[u] + w;
dfs(v, u);
}
}
inline int lca(int u, int v)
{
if (dep[u] < dep[v]) swap(u, v);
for (re i= log2(n); i >= 0; i --)
if (dep[f[u][i]] >= dep[v]) u = f[u][i];
if (u == v) return u;
for (re i = log2(n); i >= 0; i --)
if (f[u][i] != f[v][i]) u = f[u][i], v = f[v][i];
return f[u][0];
}
/*
void cale(int u, int fa)
{
for (re i = h[u]; i; i = e[i].next)
{
int v = e[i].to;
if (v == fa) continue;
cale(v, u);
d[u] += d[v];
}
}
*/
inline bool check(int mid)
{
memset(d, 0, sizeof(d));
int cnt = 0, maxt = 0;
for (re i = 1; i <= m; i ++)
{
int x = a[i].x, y = a[i].y, p = a[i].p;
int t = dist[x] + dist[y] - dist[p] * 2;
if (t > mid)
{
d[x] += 1;
d[y] += 1;
d[p] -= 2;
cnt ++;
maxt = max(maxt, t - mid);
}
}
if (!cnt) return true;
// cale(1, 0);
for (re i = n; i >= 1; i --)
{
int x = seq[i];
d[f[x][0]] += d[x];
}
for (re i = 2; i <= n; i ++)
if (d[i] == cnt && dist[i] - dist[f[i][0]] >= maxt) return true;
return false;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> m;
for (re i = 1; i < n; i ++)
{
int x, y, w; cin >> x >> y >> w;
add(x, y, w); add(y, x, w);
}
dfs(1, 0);
for (re i = 1; i <= m; i ++)
{
int x, y; cin >> x >> y;
a[i] = (road){x, y, lca(x, y)};
}
int l = 0, r = 3e8;
while (l < r)
{
int mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid + 1;
}
cout << l << '\n';
return 0;
}
posted @   Zhang_Wenjie  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示