zoukankan      html  css  js  c++  java
  • bzoj2282 [Sdoi2011]消防

    Description

    某个国家有n个城市,这n个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为zi(zi<=1000)。
    这个国家的人对火焰有超越宇宙的热情,所以这个国家最兴旺的行业是消防业。由于政府对国民的热情忍无可忍(大量的消防经费开销)可是却又无可奈何(总统竞选的国民支持率),所以只能想尽方法提高消防能力。
    现在这个国家的经费足以在一条边长度和不超过s的路径(两端都是城市)上建立消防枢纽,为了尽量提高枢纽的利用率,要求其他所有城市到这条路径的距离的最大值最小。
    你受命监管这个项目,你当然需要知道应该把枢纽建立在什么位置上。

    Input

    输入包含n行:
    第1行,两个正整数n和s,中间用一个空格隔开。其中n为城市的个数,s为路径长度的上界。设结点编号以此为1,2,……,n。
    从第2行到第n行,每行给出3个用空格隔开的正整数,依次表示每一条边的两个端点编号和长度。例如,“2 4 7”表示连接结点2与4的边的长度为7。

     

    Output

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

    Sample Input

    【样例输入1】
    5 2
    1 2 5
    2 3 2
    2 4 4
    2 5 3 



    【样例输入2】
    8 6
    1 3 2
    2 3 2 
    3 4 6
    4 5 3
    4 6 4
    4 7 2
    7 8 3

    Sample Output

    【样例输出1】

    5
    【样例输出2】

    5

    Hint

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

    Source

    stage 2 day1

    解法:

    首先我们要知道以下几件事情:

    1、枢纽一定在直径上。

    2、如果有多条直径,每一条都是等效的,枢纽建在不同直径上对最长距离的最小值并没有影响。

    3、直径的两个端点到枢纽的距离并不一定是最长的(我看有的大佬说错了)。

    3(变式)、当整条直径都作为枢纽的时候,答案就变成了非直径上的点到直径的最长距离。

    解释一下3:

    对于以下这张图

    假设( x ,6)是树的直径,当前枢纽已经从 x 扩展到了2。对于2来说,离枢纽最远的点是6;但当枢纽扩展到3,离枢纽最远的点就是非直径的5。

    知道这些后本题就很简单了。我们首先要找出一条直径,枚举上面的每一个点 u 作为枢纽的起点,然后向下扩展,找到满足条件最远的 v 。对于每一组( u , v )计算出直径的两个端点到 u 和 v 的距离,最后再对非直径上的点到直径的最远距离取一个 max 即可。

    AC代码(略丑轻喷):

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    
    using namespace std;
    
    int n,s,num_edge,head[300008],dis[300008],maxx=-1,x,y,pre[300008],ans;
    
    bool pd[300008];
    
    struct Edge{
        int from,to,dis,next;
    }edge[600008];
    
    long long read()
    {
        long long 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;
    }
    
    void addedge(int from,int to,int dis)
    {
        edge[++num_edge].next=head[from];
        edge[num_edge].from=from;
        edge[num_edge].to=to;
        edge[num_edge].dis=dis;
        head[from]=num_edge;
    }
    
    void dfs(int xx,int fa,int f)//寻找直径 
    {
        for(int i=head[xx];i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(v==fa) continue;
            if(f) pre[v]=xx;
            dis[v]=dis[xx]+edge[i].dis;
            dfs(v,xx,f);
        }
    }
    
    int find(int xx,int len)//判断核的大小 
    {
        for(int i=head[xx];i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(v==pre[xx]&&len>=edge[i].dis)
            {
                return find(v,len-edge[i].dis);
            }
        }
        return xx;
    }
    
    int work(int xx,int len)//寻找最长距离 
    {
        int num1=dis[xx],po=find(xx,len);
        int num2=dis[x]-dis[po];
        return max(num1,num2);
    }
    
    void ask(int xx,int fa)//寻找其他点到直径上的最大距离 
    {
        for(int i=head[xx];i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(pd[v]) continue;
            if(v==fa) continue;
            dis[v]=dis[xx]+edge[i].dis;
            ask(v,xx);
        }
    }
    
    int main()
    {
        n=read();s=read();
        for(int i=1;i<n;++i)
        {
            int a,b,c;
            a=read();b=read();c=read();
            addedge(a,b,c);
            addedge(b,a,c);
        }
        dfs(1,0,0);//寻找直径一点 
        for(int i=1;i<=n;++i)
            if(dis[i]>maxx)
            {
                x=i;
                maxx=dis[i];
            }
        maxx=-1;
        dis[x]=0;
        dfs(x,0,1);//寻找直径另一点 
        for(int i=1;i<=n;++i)
            if(dis[i]>maxx)
            {
                maxx=dis[i];
                y=i;
            }
        memset(dis,0,sizeof(dis));
        int now=y;
        dis[now]=0;
        pd[now]=1;
        while(now!=x)//处理直径上每一个点到y的距离 
        {
            for(int j=head[now];j;j=edge[j].next)
            {
                int v=edge[j].to;
                if(v==pre[now])
                {
                    pd[v]=1;
                    dis[v]=dis[now]+edge[j].dis;
                }
            }
            now=pre[now];
        }
        now=y;
        int ans=1e9;
        while(now!=x)//处理直径端点的距离
        {
            ans=min(ans,work(now,s));
            now=pre[now];
        }
        ans=min(ans,work(x,s));
        memset(dis,0,sizeof(dis));
        now=y;
        while(now!=x)//处理其他点到直径的距离 
        {
            ask(now,0);
            now=pre[now];
        }
        ask(x,0);
        for(int i=1;i<=n;++i)
            ans=max(ans,dis[i]);
        printf("%d",ans);
        return 0;
    }

    最后偷偷告诉你们,其实这道题每一个数据的 s 都大于直径的总长度,问题就变成了寻找非直径上的点到直径的最远距离了,两个 dfs 就可以解决。

  • 相关阅读:
    第三天 moyax
    mkfs.ext3 option
    write file to stroage trigger kernel warning
    download fomat install rootfs script
    custom usb-seriel udev relus for compatible usb-seriel devices using kermit
    Wifi Troughput Test using iperf
    learning uboot switch to standby system using button
    learning uboot support web http function in qca4531 cpu
    learngin uboot design parameter recovery mechanism
    learning uboot auto switch to stanbdy system in qca4531 cpu
  • 原文地址:https://www.cnblogs.com/-hhs/p/11730498.html
Copyright © 2011-2022 走看看