bzoj2282 [SDOI2011]消防
题目
题解
首先吐槽一发题意
选择的路径可以不包含边???
只包含一个节点也算路径???
惹不起 惹不起
然后我们就可以发现,离任意一点最远的点必然是直径的端点,所以所选路径至少有一点在树的直径上
此时答案即为较远端点到当前点的距离
剩下的点若不在直径上,无法更新直径端点到所选路径的距离,答案不变
所以整条路径都在直径上答案是最优的
于是二分
(树的直径两遍bfs求得)
代码
#include <bits/stdc++.h> using namespace std; const int N = 300005; const int M = 2 * N; inline int read() { int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();} while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();} return x * f; } int n, len; struct node { int fr, to, w, nt; node(int ff = 0, int tt = 0, int ww = 0, int nn = 0) {fr = ff; to = tt; w = ww; nt = nn;} }E[M]; int num, p[N]; void add(int x, int y, int z) {E[++num] = node(x, y, z, p[x]); p[x] = num;} int dis[N], from[N]; queue<int> q; bool mark[N]; void bfs(int s) { memset(dis, -1, sizeof(dis)); q.push(s); dis[s] = 0; while(!q.empty()) { int x = q.front(); q.pop(); for(int e = p[x]; e; e = E[e].nt) { int k = E[e].to; if(dis[k] != -1) continue; from[k] = x; q.push(k); if(mark[E[e].to]) dis[k] = dis[x]; else dis[k] = dis[x] + E[e].w; } } } int dia, st[N];//st[i]记录i到s的距离 void get_dia() { int s = 0, t = 0; bfs(1); for(int i = 1; i <= n; i++) if(dis[i] > dis[s]) s = i; bfs(s); for(int i = 1; i <= n; i++) if(dis[i] > dis[t]) t = i; dia = dis[t]; st[++st[0]] = dis[t]; mark[t] = true; while(t != s) { st[++st[0]] = dis[from[t]]; t = from[t]; mark[t] = true; } bfs(s); } bool check(int mid) { int l = 1, r = st[0]; while(st[1] - st[l + 1] <= mid && l <= st[0]) l++; while(st[r - 1] <= mid && r >= 1) r--; return st[l] - st[r] <= len; } int main() { n = read(); len = read(); for(int i = 1; i < n; i++) { int x = read(), y = read(), z = read(); add(x, y, z); add(y, x, z); } get_dia(); int l = 0, r = dia, ans = 0; for(int i = 1; i <= n; i++) ans = max(ans, dis[i]); if(len < dia) while(l <= r) { int mid = (l + r) >> 1; if(check(mid)) {ans = mid; r = mid - 1;} else l = mid + 1; } printf("%d", ans); return 0; }