bzoj2282: [Sdoi2011]消防

Posted WQHui

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 }

 

以上是关于bzoj2282: [Sdoi2011]消防的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ2282[Sdoi2011]消防 树形DP+双指针法+单调队列

BZOJ 2282 [Sdoi2011]消防

bzoj2282 [SDOI2011]消防

消防(bzoj 2282)

[SDOI2011]消防

[Sdoi2011]消防