[SDOI2011]消防

Posted 弥生三月

tags:

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

 

思路很好的题,推荐一下
首先,答案路径一定在直径上,这个我不会严谨的证明,只有一个思路是这样的:
分两种情况:

1、其中一个端点不在直径上,那么从那个在直径的端点上dfs一遍,那么那个不在直径上的点的子树里一定会有某个点比直径的另一端还要远,不然这样不会是最优答案,这与直径的定义矛盾
2、两个端点都不在直径上,思路和上一个差不多
有了这个性质,显然答案的下界就是树上所有不在直径上的点到直径距离的最大值mx_l,这个把直径上的边长度设为0,dfs一遍就好了
答案的上界更显然,就是直径的长度len
现在唯一的问题就是,直径可能太长,不满足路径长度小于s这个限制
然后我们考虑一个直径的性质——从树上上任何一个点开始dfs,距离最远的一个点一定是直径的两个端点之一,那么如果我们把直径的端点向里“缩”一些距离w,这个距离至少要是mx_l,我们的最大距离也不会超过w!因为如果这个距离超过了w,那些原来走到直径上的终点没有被缩掉的点,到直径的距离不会变,变的只可能因为原来的终点不在路径上了,所以距离变长了,这样的话终点只可能是当前路径的端点,那从这个端点开始dfs,某个点会比原来直径的端点还远,这是不符合直径的性质的。
然后我们就可以二分这个w,如果直径两边各往里缩w之后长度<=s,就是可行

因为据说会爆栈?所以我写了BFS

 

  1 #include <iostream>
  2 #include <cstdlib>
  3 #include <cstdio>
  4 #include <algorithm>
  5 #include <string>
  6 #include <cstring>
  7 #include <cmath>
  8 #include <map>
  9 #include <stack>
 10 #include <set>
 11 #include <vector>
 12 #include <queue>
 13 #include <time.h>
 14 #define eps 1e-7
 15 #define INF 0x3f3f3f3f
 16 #define MOD 1000000007
 17 #define rep0(j,n) for(int j=0;j<n;++j)
 18 #define rep1(j,n) for(int j=1;j<=n;++j)
 19 #define pb push_back
 20 #define set0(n) memset(n,0,sizeof(n))
 21 #define ll long long
 22 #define ull unsigned long long
 23 #define iter(i,v) for(edge *i=head[v];i;i=i->nxt)
 24 #define max(a,b) (a>b?a:b)
 25 #define min(a,b) (a<b?a:b)
 26 #define print_rumtime printf("Running time:%.3lfs\n",double(clock())/1000.0);
 27 #define TO(j) printf(#j": %d\n",j);
 28 //#define OJ
 29 using namespace std;
 30 const int MAXINT = 100010;
 31 const int MAXNODE = 300010;
 32 const int MAXEDGE = 2*MAXNODE;
 33 char BUF,*buf;
 34 int read(){
 35     char c=getchar();int f=1,x=0;
 36     while(!isdigit(c)){if(c==-) f=-1;c=getchar();}
 37     while(isdigit(c)){x=x*10+c-0;c=getchar();}
 38     return f*x;
 39 }
 40 char get_ch(){
 41     char c=getchar();
 42     while(!isalpha(c)) c=getchar();
 43     return c;
 44 }
 45 //------------------- Head Files ----------------------//
 46 int n,cnt,dis[MAXNODE],q[MAXNODE],vis[MAXINT],far,mx,p1,p2,len,s;
 47 
 48 struct edge{
 49     int u,v,l,bak;
 50     edge *nxt;
 51     edge(){}
 52     edge(int _v,int _u,int _l,edge *_nxt):u(_u),v(_v),l(_l),nxt(_nxt){}
 53 }mp[MAXEDGE],*head[MAXNODE],*from[MAXNODE],*to[MAXNODE];
 54 void add_edge(int,int,int);
 55 void get_input();
 56 void work();
 57 int bfs(int);
 58 int check(int);
 59 int main() {
 60     get_input();
 61     work();
 62     return 0;
 63 }
 64 void work(){
 65     int p = p2;
 66     while(p!=p1){
 67         from[p]->bak=from[p]->l;
 68         from[p]->l=0;
 69         to[from[p]->u]=from[p];
 70         p=from[p]->u;
 71     }
 72     while(p!=p1){
 73         from[p]->l=from[p]->bak;
 74         p=from[p]->u;
 75     }
 76     bfs(p1);
 77     int lb=mx-1,rb=len;
 78     while(rb-lb>0){
 79         int mid = (lb+rb)/2;
 80         if(check(mid)) rb=mid;
 81         else lb=mid;
 82     }
 83     printf("%d\n",rb);
 84 }
 85 void get_input(){
 86     n=read();s=read();
 87     rep0(i,n-1){
 88         int u=read(),v=read(),l=read();
 89         add_edge(u,v,l);
 90     }
 91     p1=bfs(1);p2=bfs(p1);
 92     len=mx;
 93 }
 94 void add_edge(int u,int v,int l){
 95     mp[cnt]=edge(u,v,l,head[u]);
 96     head[u]=&mp[cnt++];
 97     mp[cnt]=edge(v,u,l,head[v]);
 98     head[v]=&mp[cnt++];
 99 }
100 int bfs(int p){
101     int *h,*t;
102     memset(vis,0,sizeof(vis));
103     memset(dis,INF,sizeof(dis));
104     h=t=q;
105     *t++=p;
106     vis[p]=1;
107     dis[p]=0;
108     while(h!=t){
109         p=*h++;
110         iter(i,p){
111             if(!vis[i->v]){
112                 vis[i->v]=1;
113                 dis[i->v]=dis[p]+i->l;
114                 *t++=i->v;
115                 from[i->v]=i;
116             }
117         }
118     }
119     mx=-1;
120     rep1(i,n){
121         if(dis[i]>mx){
122             mx=dis[i];
123             far=i;
124         }
125     }
126     return far;
127 }
128 int check(int mxl){
129     int pl=len;
130     for(int t=mxl,p=p1;to[p]&&t>=to[p]->l;t-=to[p]->l,pl-=to[p]->l,p=to[p]->v);
131     for(int t=mxl,p=p2;from[p]&&t>=from[p]->l;t-=from[p]->l,pl-=from[p]->l,p=from[p]->u);
132     return pl<=s;
133 }

 

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

[Sdoi2011]消防

SDOI2011 第2轮 DAY1消防 [解题报告]

[SDOI2011]消防

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

[BZOJ2282] [Sdoi2011]消防

[SDOI2011]消防(单调队列,树的直径,双指针)