首页 > 其他 > 详细

[BZOJ2282]消防

时间:2018-07-31 20:39:01      阅读:155      评论:0      收藏:0      [点我收藏+]

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

 

首先我们应该想到这条路径一定在树的直径上。这里给出证明:

设直径的长度为d,那么直径外的边长度一定小于等于d/2(否则该路径会与直径的一部分构成一条比直径还长的路径)

若该路径不在直径上,则直径的最远点到该路径的距离至少为d/2

而如果在直径上,一定存在一段路径使得树上所有点的距离到它不超过d/2

所以选在直径上一定是优的,而且在符合题目要求的情况下越长越好。

那么如果我们确定里直径,我们可以O(n)求出直径上每个点到最远点的距离,然后左右指针扫直径,单调队列维护区间最小值即可。

代码:

  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdio>
  4 #include<queue>
  5 using namespace std;
  6 deque<int>q;
  7 struct point
  8 {
  9     int to;
 10     int next;
 11     int dis;
 12 }e[1000010];
 13 int n,x,y,z,res=100000010,maxn,s,t,num,ss;
 14 int d[500001],head[500001],f[500001],pre[500001];
 15 bool vis[500001];
 16 void add(int from,int to,int dis)
 17 {
 18     e[++num].next=head[from];
 19     e[num].to=to;
 20     e[num].dis=dis;
 21     head[from]=num;
 22 }
 23 void dfs(int x)
 24 {
 25     vis[x]=true;
 26     for(int i=head[x];i!=0;i=e[i].next)
 27     {
 28         int to=e[i].to;
 29         if(!vis[to])
 30         {
 31             d[to]=d[x]+e[i].dis;
 32             dfs(to);
 33         }
 34     }
 35 }
 36 void dfs1(int x)
 37 {
 38     vis[x]=true;
 39     for(int i=head[x];i!=0;i=e[i].next)
 40     {
 41         int to=e[i].to;
 42         if(!vis[to])
 43         {
 44             pre[to]=x;
 45             d[to]=d[x]+e[i].dis;
 46             dfs1(to);
 47         }
 48     }
 49 }
 50 void find(int x)
 51 {
 52     vis[x]=true;
 53     for(int i=head[x];i!=0;i=e[i].next)
 54     {
 55         int to=e[i].to;
 56         if(!vis[to])
 57         {
 58             find(to);
 59             d[x]=max(d[x],d[to]+e[i].dis);
 60         }
 61     }
 62 }
 63 void getf()
 64 {
 65     int slr=0;
 66     for(int i=t;i!=0;i=pre[i])
 67     {
 68         f[i]=slr;
 69         for(int j=head[i];j!=0;j=e[j].next)
 70         {
 71             int to=e[j].to;
 72             if(pre[i]==to)
 73             {
 74                 slr+=e[j].dis;
 75                 break;
 76             }    
 77         }
 78     }
 79 }
 80 int main()
 81 {
 82     scanf("%d%d",&n,&ss);
 83     for(int i=1;i<=n-1;i++)
 84     {
 85         scanf("%d%d%d",&x,&y,&z);
 86         add(x,y,z);
 87         add(y,x,z);
 88     }
 89     dfs(1);
 90     for(int i=1;i<=n;i++)
 91     {
 92         if(d[i]>maxn)
 93         {
 94             maxn=d[i];
 95             s=i;
 96         }
 97     }
 98     memset(vis,false,sizeof(vis));
 99     memset(d,0,sizeof(d));
100     dfs1(s);
101     maxn=0;
102     for(int i=1;i<=n;i++)
103         if(d[i]>maxn)
104         {
105             maxn=d[i];
106             t=i;
107         }
108     memset(vis,false,sizeof(vis));
109     memset(d,0,sizeof(d));
110     for(int i=t;i!=0;i=pre[i])
111         vis[i]=true;
112     for(int i=t;i!=0;i=pre[i])
113         find(i);
114     getf();
115     int r=t,l=pre[t];
116     q.push_front(l);
117     if(d[l]<d[r])
118         q.push_back(r);
119     while(1)
120     {
121         if(pre[l]!=0&&f[pre[l]]-f[r]<=ss)
122         {
123             l=pre[l];
124             int ans=max(f[r],maxn-f[l]);
125             q.push_front(l);
126             while(d[q.back()]<d[l])
127                 q.pop_back();
128             ans=max(ans,d[q.back()]);
129             res=min(res,ans);
130         }
131         else if(l!=r)
132         {
133             if(q.back()==r)
134                 q.pop_back();
135             r=pre[r];
136             int ans=max(f[r],maxn-f[l]);
137             ans=max(ans,d[q.back()]);
138             res=min(res,ans);
139         }
140         else if(l==r&&pre[l])
141         {
142             while(!q.empty())
143                 q.pop_back();
144             l=pre[l];
145             r=l;
146             q.push_front(l);
147             int ans=max(f[r],maxn-f[l]);
148             ans=max(ans,d[q.back()]);
149             res=min(res,ans);
150         }
151         else
152             break;
153     }
154     printf("%d",res);
155     return 0;
156 }

这道题综合了许多算法,也有一定的思维难度,是一道不可多得好题

[BZOJ2282]消防

原文:https://www.cnblogs.com/Slrslr/p/9397895.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!