[Sdoi2011]消防
Posted cutemush
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Sdoi2011]消防相关的知识,希望对你有一定的参考价值。
Time Limit: 10 Sec Memory Limit: 512 MB
某个国家有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。
/* 证明1:对于直径上的点,离它最远的点一定是直径上的某个点 假设直径为ac e . . . a.......b.....c(设ab>bc) . . . . d 对于b点来说,离它最远的应该是a点 如果不是a,而是d,则(c,b,d)应该是直径 证明2:对于非直径上的点d, 离它最远的点也一定是直径上的某个点 假如离d最远的不是a,而是e. 则db+be>db+ab 于是be>ab,也就是说e离b,比a离b更远,这也开始的证明相违背。 l...........t.......i.........r l,r为直径的左右端点,i为我们枚举的一个点,t为另一个点(初值为i的父亲点) [t,i]的距离不超过规定的Len 则直径上没有被选中的,到选中的边的距离,按要求尽可能小,于是取 ans=min(ans,max(d[t],d[r]-d[i])); d数组代表每个点到l的距离。 由于树上所有点,离它们最远的点,一定是直径的两个端点之一,所以一般来说这样求出来后就可以了 但是考虑到给定的Len可能覆盖整个路径。于是求出来的ans为0 于是我们还要将直径上的点一个个拿出来,求出其它不在直径上点到它们的距离 再找出这些距离的最大值, 再与ans取个最大值。 */ #include<iostream> #include<cstdio> #include<cstring> using namespace std; int fa[310100],vis[301000],last[301000],len=0,d[301000]; struct node { int to,next,w; }a[601000]; void add(int a1,int a2,int a3) { len++; a[len].to=a2; a[len].w=a3; a[len].next=last[a1]; last[a1]=len; } void dfs(int x) { for(int i=last[x];i;i=a[i].next) { int to=a[i].to; if(vis[to]||fa[x]==to) continue; fa[to]=x; d[to]=d[x]+a[i].w; dfs(to); } } int main() { int n,s,x,y,z,ans=2147483647; cin>>n>>s; for(int i=1;i<n;i++) { scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,z); } int l=1,r=1; dfs(l); for(int i=1;i<=n;i++) if(d[i]>d[l]) l=i; memset(fa,0,sizeof(fa)); d[l]=0; dfs(l);//算出每个点到L的距离,l是直径的左端点 for(int i=1;i<=n;i++) if(d[i]>d[r]) r=i; int t=r; //找出直径的右端点 for(int i=r;i;i=fa[i])//尺取法 { while(fa[t]&&d[i]-d[fa[t]]<=s) t=fa[t]; ans=min(ans,max(d[t],d[r]-d[i])); } for(int i=r;i;i=fa[i]) //找出直径上的点 vis[i]=1; for(int i=r;i;i=fa[i]) d[i]=0,dfs(i);//以直径上每个点为根,找出非直径上的点到它们的距离 for(int i=1;i<=n;i++) //在找出这些距离后,求出其最大值 if(vis[i]==0) ans=max(ans,d[i]); cout<<ans; }
以上是关于[Sdoi2011]消防的主要内容,如果未能解决你的问题,请参考以下文章