bzoj 3246 [Ioi2013]Dreaming 贪心

[Ioi2013]Dreaming

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 638  Solved: 241
[Submit][Status][Discuss]

Description

Serpent(水蛇)生活的地方有N个水坑,编号为0,...,N - 1,有M条双向小路连接这些水坑。每两个水坑之间至多有一条路径(路径包含一条或多条小路)相互连接,有些水坑之间根本无法互通(即M ≤ N-1 )。Serpent走过每条小路需要一个固定的天数,不同的小路需要的天数可能不同。Serpent的朋友袋鼠希望新修 N - M - 1条小路,让Serpent可以在任何两个水坑间游走。袋鼠可以在任意两个水坑之间修路,Serpent通过每条新路的时间都是L天。袋鼠希望找到一种修路方式使得修路之后Serpent在每两个水坑之间游走的最长时间最短。

举例说明

上图中有12个水坑8条小路( N = 12, M = 8)。假如L = 2 ,即Serpent通过任何一条新路都需要2天。那么,袋鼠可以修建3条新路: 
水坑1和水坑2之间;
水坑1和水坑6之间;
水坑4和水坑10之间。

上图显示了修路后的最终状态。从水坑0走到水坑11的时间最长,需要18天。这是 最佳结果,无论袋鼠如何选择修路方式,总会存在一些水坑对,Serpent需要18天 或者更长时间从其中一个走到另一个。
 

 

Input

N : 水坑的数目。
M : 原本存在的小路的数目。
L : Serpent通过新修的路经的时间。
A, B 和 T: 三个包含M个元素的数组,分别表示每条小路的两个端点和通过这条小路的时间。例如,第i条小路连接水坑 A[i-1]和水坑B[i-1],通过这条小路的时间是T[i-1]天。
 
 

Output


如上所述,表示游走于两个距离最远的水坑之间所需的时间。

Sample Input

12 8 2
0 8 4
8 2 2
2 7 4
5 11 3
5 1 7
1 3 1
1 9 5
10 6 3

Sample Output

18

HINT

 

 n <= 500000

 

题目大意

给定n个点m条边的森林,每条边有边权。要求用长度为L的边把它连成一棵树,且直径最小。

n≤500000

分析

首先对于森林中每一棵树,它只有一条路径可能会对答案有贡献。为了使答案尽量小,那就要使这个值尽量小。那么可以选择它的所有点中,到达其它点距离最大值最小的,去和其它树连接。然后这个点对答案的贡献就是这个距离。

那么会发现,这些数看成一个点之后,又会连成一棵树。 
由于这棵树的形态是任意的,肯定是选择一个点作为根,然后其它点和根直接连接最优。为了答案尽量小,又一定是把上面所述距离的最大值作为根。

那么对答案有贡献的就只有三种情况: 
1. 原来每棵树的直径 
2. 距离最大值+距离次大值+L 
3. 距离次大值+距离第3大值+2L

 

 1 #include<cstring>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<iostream>
 5 #include<cmath>
 6 #include<queue>
 7 #include<map>
 8 
 9 #define N 500007
10 #define M 1000007
11 using namespace std;
12 inline int read()
13 {
14     int x=0,f=1;char ch=getchar();
15     while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
16     while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
17     return x*f;
18 }
19 
20 int n,m,L;
21 int tot,h[N],e[M],nxt[M],w[M],fa[N],dis[N],D[N],ans,cnt,len[N];
22 bool v[N];
23 char c;
24 
25 void add(int x,int y,int d)
26 {
27     e[++tot]=y; nxt[tot]=h[x]; w[tot]=d; h[x]=tot;
28 }
29 
30 int main()
31 {
32     n=read(); m=read(); L=read();
33     while (m--)
34     {
35         int x=read()+1,y=read()+1,w=read();
36         add(x,y,w); add(y,x,w);
37     }
38     for (int i=1;i<=n;i++) 
39         if (!v[i])
40            {    
41             D[tot=1]=i;
42                for (int j=1;j<=tot;j++)
43             {
44                 int x=D[j];
45                 v[x]=1;
46                 for (int k=h[x];k;k=nxt[k]) 
47                     if (e[k]!=fa[x]) D[++tot]=e[k],dis[e[k]]=dis[x]+w[k],fa[e[k]]=x;
48             }
49             int r=D[tot];
50             for (int j=1;j<tot;j++) 
51                 if (dis[D[j]]>dis[r]) r=D[j];
52             D[tot=1]=r; dis[r]=0; fa[r]=0;
53             for (int j=1;j<=tot;j++)
54             {
55                 int x=D[j];
56                 for (int k=h[x];k;k=nxt[k])
57                     if (e[k]!=fa[x])
58                     {
59                         D[++tot]=e[k]; 
60                         dis[e[k]]=dis[x]+w[k]; 
61                         fa[e[k]]=x;
62                     }
63             }
64             r=D[tot];
65             for (int j=1;j<tot;j++)
66                 if (dis[D[j]]>dis[r]) r=D[j];
67             ans=max(ans,dis[r]); len[++cnt]=dis[r];
68             for (int j=r;j>0;j=fa[j]) len[cnt]=min(len[cnt],max(dis[j],dis[r]-dis[j]));
69            }
70     sort(len+1,len+cnt+1);
71     if (cnt==2) ans=max(ans,len[2]+len[1]+L);
72     else if (cnt>2) ans=max(ans,max(len[cnt]+len[cnt-1]+L,len[cnt-1]+len[cnt-2]+L*2));
73     printf("%d\n",ans);
74 }

 

posted @ 2018-04-15 15:40  Kaiser-  阅读(193)  评论(0编辑  收藏  举报