• [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

  • 相关阅读:
    各种颜色的英文代码
    颜色代码简集
    [转]怎么成为优秀的软件模型设计者
    控件禁用
    jQuery图片播放插件ColorBox使用方法
    5个好玩的在线HTML5游戏【部分附源码下载】
    各大网站架构总结笔记(续)
    Web开发基础务实之《ASP.NET战役完胜表彰晚会(一)》
    iview Form自动跳转到校验不通过的地方
    C++与Java比较
  • 原文地址:https://www.cnblogs.com/kzj-pwq/p/9512073.html
Copyright © 2020-2023  润新知