2282: [Sdoi2011]消防
Time Limit: 10 Sec Memory Limit: 512 MBDescription
Input
输入包含n行:
第1行,两个正整数n和s,中间用一个空格隔开。其中n为城市的个数,s为路径长度的上界。设结点编号以此为1,2,……,n。
从第2行到第n行,每行给出3个用空格隔开的正整数,依次表示每一条边的两个端点编号和长度。例如,“2 4 7”表示连接结点2与4的边的长度为7。
Output
输出包含一个非负整数,即所有城市到选择的路径的最大值,当然这个最大值必须是所有方案中最小的。
Sample Input
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
5
【样例输出2】
5
HINT
对于100%的数据,n<=300000,边长小等于1000。
Source
建立消防枢纽一定是建在树的直径上的。(为什么?请读者自行思考)
对于求树的直径两遍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 }