P2680 [NOIP2015 提高组] 运输计划
P2680 [NOIP2015 提高组] 运输计划
给定一棵边带权的树,以及 \(m\) 条路径,可以将某条边的边权变为 \(0\),求最大路径和的最小值。
一开始看错题了,导致一度认为是水题,但是更正题意后发现其实就套了个二分。
最大值最小想到二分答案,判定的时候直接树上差分,找到经过所有 \(>mid\) 的路径的边中边权最大的。
然后用最大路径和 - 边权来判断即可,这个思想比较巧妙,因为最大的满足了,其它一定也满足。
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 300010;
int n, m, cnt, num, mxp; LL len, dis[N];
int head[N], d[N], dep[N], fa[N][30];
struct Edge{int nxt, to, val;} ed[N << 1];
struct Modify{int x, y; LL dis;} a[N];
int read(){
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x * f;
}
void add(int u, int v, int w){
ed[++ cnt] = (Edge){head[u], v, w};
head[u] = cnt;
}
void dfs1(int u, int Fa){
dep[u] = dep[Fa] + 1; fa[u][0] = Fa;
for(int i = 1; (1 << i) <= dep[u]; i ++)
fa[u][i] = fa[fa[u][i - 1]][i - 1];
for(int i = head[u], v; i; i = ed[i].nxt)
if((v = ed[i].to) != Fa){
dis[v] = dis[u] + ed[i].val;
dfs1(v, u);
}
}
int Lca(int x, int y){
if(dep[x] > dep[y]) swap(x, y);
for(int i = 20; i >= 0; i --)
if(dep[fa[y][i]] >= dep[x]) y= fa[y][i];
if(x == y) return x;
for(int i = 20; i >= 0; i --)
if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
void dfs2(int u, int Fa){
for(int i = head[u]; i; i = ed[i].nxt){
int v = ed[i].to, w = ed[i].val;
if(v == Fa) continue;
dfs2(v, u); d[u] += d[v];
if(d[v] == num) mxp = max(mxp, w);
}
}
bool chck(LL mid){
for(int i = 1; i <= n; i ++) d[i] = 0;
mxp = 0, num = 0;
for(int i = 1; i <= m; i ++)
if(a[i].dis > mid){
num ++; int x = a[i].x, y = a[i].y;
d[x] ++, d[y] ++, d[Lca(x, y)] -= 2;
}
dfs2(1, 0);
return (len - mxp <= mid);
}
int main(){
n = read(), m = read();
for(int i = 1; i < n; i ++){
int u = read(), v = read(), w = read();
add(u, v, w), add(v, u, w);
}
dfs1(1, 0);
LL l = 0, r = 0;
for(int i = 1; i <= m; i ++){
int u = read(), v = read();
LL w = 1LL * dis[u] + dis[v] - 2LL * dis[Lca(u, v)];
a[i] = (Modify){u, v, w}; r = max(r, w);
}
len = r;
while(l < r){
LL mid = (l + r) >> 1;
if(chck(mid)) r = mid;
else l = mid + 1;
}
printf("%lld\n", l);
return 0;
}