[SDOI2011] 消防

题目描述

某个国家有n个城市,这n个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为zi(zi<=1000)。

这个国家的人对火焰有超越宇宙的热情,所以这个国家最兴旺的行业是消防业。由于政府对国民的热情忍无可忍(大量的消防经费开销)可是却又无可奈何(总统竞选的国民支持率),所以只能想尽方法提高消防能力。

现在这个国家的经费足以在一条边长度和不超过s的路径(两端都是城市)上建立消防枢纽,为了尽量提高枢纽的利用率,要求其他所有城市到这条路径的距离的最大值最小。

你受命监管这个项目,你当然需要知道应该把枢纽建立在什么位置上。

输入输出格式

输入格式:

输入包含n行:

第1行,两个正整数n和s,中间用一个空格隔开。其中n为城市的个数,s为路径长度的上界。设结点编号以此为1,2,……,n。

从第2行到第n行,每行给出3个用空格隔开的正整数,依次表示每一条边的两个端点编号和长度。例如,“2 4 7”表示连接结点2与4的边的长度为7。

输出格式:

输出包含一个非负整数,即所有城市到选择的路径的最大值,当然这个最大值必须是所有方案中最小的。

输入输出样例

输入样例#1: 复制

5 2
1 2 5
2 3 2
2 4 4
2 5 3

输出样例#1: 复制

5

输入样例#2: 复制

8 6
1 3 2
2 3 2
3 4 6
4 5 3
4 6 4
4 7 2
7 8 3

输出样例#2: 复制

5

说明

【数据规模和约定】

对于20%的数据,n<=300。

对于50%的数据,n<=3000。

对于100%的数据,n<=300000,边长小等于1000。

Solution

显然,我们选的在直径上是最优的(简单证明:假设我们选7-6(长度为6),还不如只选7-4(长度为2),因为还可以留下多余长度去选更多的点,为什么说还不如只选7-4呢?因为4-6长度不可能大于4-2的长度,因为2是直径的上的点),把样例2直径画出来,如下图:

2 3 4 7 8为直径,接下来我们就可以用尺取法贪心在直径上求出很多条长度小于等于s的路径,根据图,我们可以选出8-4长度为5,4-3长度为6,3-2长度为2这些路径。当我们选8-4时,显然2点离选的路径最远也就是直径的一端,显然1,5,6这三个不在直径上的点不会比直径的一端2点离选的这条路径更远,因为如果更远的话,说明之前求得不是树上最长路,也就是不是直径。同理,我们先不需考虑不在直径上的点。但是我们在最后还是需要考虑不是在直径上的点,例如样例2的s为inf,也就是可以选无限长,那么我们可以选直径2 3 4 7 8,如果我们不考虑不在直径上的点,那么答案为0,显然答案为4(4点到6点距离),我们只需求出不在直径上的点离直径有多远就行了。

时间复杂度 O(n)

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int fa[310100],vis[301000],last[301000],len=0,d[301000];
struct node
{
    int to,next,w;
}a[601000];
void add(int a1,int a2,int a3)
{
    len++;
    a[len].to=a2;
    a[len].w=a3;
    a[len].next=last[a1];
    last[a1]=len;
}
void dfs(int x)
{
    for(int i=last[x];i;i=a[i].next)
    {
        int to=a[i].to;
        if(vis[to]||fa[x]==to) continue;
        fa[to]=x;
        d[to]=d[x]+a[i].w;
        dfs(to);
    }
}
int main()
{	
    int n,s,x,y,z,ans=2147483647;
    cin>>n>>s;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
        add(y,x,z);
    }
    int l=1,r=1;
    dfs(l);
    for(int i=1;i<=n;i++)
    if(d[i]>d[l]) l=i;
    memset(fa,0,sizeof(fa));
    d[l]=0;dfs(l);
    for(int i=1;i<=n;i++)
    if(d[i]>d[r]) r=i;
    int t=r;
    //cout<<r<<endl;
    //cout<<d[r]<<endl;
    for(int i=r;i;i=fa[i])//尺取法
    {
        while(fa[t]&&d[i]-d[fa[t]]<=s) t=fa[t];
        ans=min(ans,max(d[t],d[r]-d[i]));
    }
    for(int i=r;i;i=fa[i]) vis[i]=1;
    for(int i=r;i;i=fa[i]) d[i]=0,dfs(i);
    for(int i=1;i<=n;i++)
    if(!vis[i])
    ans=max(ans,d[i]);
    cout<<ans;
}

博主蒟蒻,可以随意转载,但必须附上原文链接k-z-j

posted @ 2018-08-21 16:21  k-z-j  阅读(397)  评论(2编辑  收藏  举报