【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;
}

  

posted @ 2016-07-23 19:45  Yangjiyuan  阅读(197)  评论(0编辑  收藏  举报