【bzoj2282】[Sdoi2011]消防
两次bfs可得直径,答案一定不会小于所有点到直径的距离最大值,只要把直径上的边权设为0,任选直径上一点bfs可得将最大值作为二分下界,二分直径左右端点的舍弃部分
#include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #include<queue> using namespace std; #define N 300010 struct edge { int to,next,w; }e[N<<1]; int head[N<<1]; int cnt; int n,s; int rt,x,y,z; int maxn,top,D; int st[N],from[N],mark[N],dis[N];//,q[N]; queue<int>q; void link(int x,int y,int z) { e[++cnt]=(edge){y,head[x],z}; head[x]=cnt; } void bfs(int x) { for (int i=1;i<=n;i++) dis[i]=-1; q.push(x); dis[x]=0; while (!q.empty()) { int now=q.front(); q.pop(); for (int i=head[now];i;i=e[i].next) { int t=e[i].to; if (dis[t]==-1) { from[t]=now; if (mark[t]) dis[t]=dis[now]; else dis[t]=dis[now]+e[i].w; q.push(t); } } } } bool work(int d) { int l=1,r=top; while (st[1]-st[l+1]<=d && l<=top) l++; while (st[r-1]<=d && r>=1) r--; return st[l]-st[r]<=s; } int main() { scanf("%d%d",&n,&s); for (int i=1;i<n;i++) { scanf("%d%d%d",&x,&y,&z); link(x,y,z); link(y,x,z); } bfs(1); for (int i=1;i<=n;i++) if (dis[rt]<dis[i]) rt=i; bfs(rt); for (int i=1;i<=n;i++) if (dis[x]<dis[i]) x=i; D=dis[x]; st[++top]=dis[x]; mark[x]=1; while (x!=rt) { st[++top]=dis[from[x]]; x=from[x]; mark[x]=1; } bfs(x); int l=0,r=D; for(int i=1;i<=n;i++) l=max(l,dis[i]); if (s<D) while (l<=r) { int mid=(l+r)>>1; if (work(mid)) r=mid-1; else l=mid+1; } printf("%d\n",l); return 0; }