• bzoj2282: [Sdoi2011]消防


    2282: [Sdoi2011]消防

    Time Limit: 10 Sec  Memory Limit: 512 MB

    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

    建立消防枢纽一定是建在树的直径上的。(为什么?请读者自行思考)

    对于求树的直径两遍DFS,第一遍DFS随便找一个点为起始点,那么它所到达的最远点一定是直径的一个端点。(为什么?请读者自行思考 or 百度)

    答案具有单调性(如果ans-1成立,那么ans就一定成立),我们可以二分求答案;

    L=max(不在直径上的点到直径的距离),R=直径的距离;

    若R<=S,那么答案就是L;  若R>S 就二分答案

    怎么判断答案是否可行呢,在直径上的左端点最多能到的点a(也就是距离小于等于ans的最远点),右端点最多能到的点b;

    若a与b的距离小于m,那么就是可行的,只需将a到b建立消防枢纽即可;

    为什么一定是直径的左端点和右端点?因为如果 直径上的叉枝到a的距离 大于 左端点到a的距离,那么它就不是直径了;

    所以从左端点和右端点开始延伸一定是最优解;

    细节见代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<algorithm>
     5 #define MAXN 2000008
     6 using namespace std;
     7 
     8 int n,m,tot,head[MAXN],next[MAXN],vet[MAXN],len[MAXN],q[MAXN],st,ed,k;
     9 int s[MAXN],dis[MAXN],from[MAXN],mark[MAXN],rt,l,r,ans,cnt;
    10 
    11 inline int read(){
    12     char ch=getchar(); int f=1,x=0;
    13     while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    14     while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    15     return x*f;
    16 }
    17 
    18 void add(int x,int y,int z){
    19     tot++;
    20     next[tot]=head[x];
    21     head[x]=tot;
    22     vet[tot]=y;
    23     len[tot]=z;
    24 }
    25 
    26 void bfs(int x){
    27     memset(dis,-1,sizeof(dis));
    28     st=0; ed=1;
    29     dis[x]=0;
    30     q[1]=x;
    31     while(st<ed){
    32         int u=q[++st];
    33         for(int i=head[u];i;i=next[i]){
    34             int y=vet[i];
    35             if(dis[y]==-1){
    36                 if(!mark[y]) dis[y]=dis[u]+len[i];
    37                 else dis[y]=dis[u];
    38                 ed++;
    39                 q[ed]=y;
    40                 from[y]=u;
    41             }
    42         }
    43     }
    44 }
    45 
    46 bool check(int x){
    47     int ll=1,rr=cnt;
    48     while(s[1]-s[ll+1]<=x&&ll<=rr) ll++;
    49     while(s[rr-1]-s[cnt]<=x&&ll<=rr) rr--;
    50     if(ll>rr) return 1;
    51     else return s[ll]-s[rr]<=m;
    52 }
    53 
    54 int main(){
    55     n=read(); m=read();
    56     for(int i=1;i<n;i++){
    57         int x,y,z;
    58         x=read(); y=read(); z=read();
    59         add(x,y,z); add(y,x,z);
    60     }
    61     rt=0;
    62     bfs(1);
    63     for(int i=1;i<=n;i++)
    64         if(dis[i]>dis[rt]) rt=i;
    65     bfs(rt);
    66     k=0;
    67     for(int i=1;i<=n;i++)
    68         if(dis[i]>dis[k]) k=i;
    69     r=dis[k];
    70     while(k!=rt){
    71         s[++cnt]=dis[k];
    72         mark[k]=1;
    73         k=from[k];
    74     }
    75     s[++cnt]=0;
    76     mark[k]=1;
    77     bfs(k);
    78     for(int i=1;i<=n;i++) l=max(dis[i],l);
    79     if(r<m){
    80         printf("%d",l);
    81         return 0;
    82     }
    83     while(l<=r){
    84         int mid=(l+r)>>1;
    85         if(check(mid)){
    86             ans=mid;
    87             r=mid-1;
    88         }else{
    89             l=mid+1;
    90         }
    91     }
    92     printf("%d",ans);
    93 }
  • 相关阅读:
    【模板】辗转相除法
    【模板】冰茶姬(大概是全的?)
    【模板】快速幂
    【模板】线段树-区间修改
    【模板】线段树-单点修改,区间查询
    【模板】dijkstra与floyd
    【模板】SPFA(不完全详解)
    【济南集训】随机分数
    P3205 [HNOI2010]合唱队
    最大子列2
  • 原文地址:https://www.cnblogs.com/WQHui/p/8576250.html
Copyright © 2020-2023  润新知