2012年海淀区第四题施工方案

施工方案(minlen)

【问题描述】

c国边防军在边境某处的阵地是由n个地堡组成的。工兵连受命来到阵地要进行两期施工。

第一期的任务是挖掘暗道让所有地堡互联互通。现已勘测设计了m条互不相交的暗道挖掘方案,如果这m条暗道都实施挖掘,肯定能达到互联互通的目的。事实上,适当选择其中n-1个方案挖掘,就能实现互联互通,即从每个地堡出发都能到达其他任何一个地堡(允许经过别的地堡)。

连长精心谋算,在m个设计规划中选取了挖掘总距离最短且能保证互联互通的若干个暗道规划实施了挖掘,完成了第一期的施工任务后又接受了第二期的施工任务,要求选择一个地堡进行扩建改造,使其能像每个地堡提供弹药。为了让弹药供应更及时、更快捷,从改扩建的地堡到最远地堡的距离(称为最远输送距离)应当尽量小。

你的任务是先求出第一期施工挖掘的总距离,再求改扩建地堡最远输送距离的最小值。

【输入格式】

其中第一行是n和m,m>=n

下面的m行每行3个数xi、yi、zi,表示xi到yi的距离是zi

  zi<1000000且m个距离互不相等

【输出格式】

共包含两行,每行一个整数,

第一行是第一期的挖掘总距离,第二行是最远输送距离的最小值。

【样例输入】

4 5

1 2 1

2 3 2

3 4 3

4 1 4

3 1 5

【样例输出】

6

3

【样例说明】

第一期挖掘1到2、2到3和3到4的3条暗道,第二期选择3号地堡进行改扩建,最远输送距离是3

【数据规模】

60%的数据 n<10且m<20

80%的数据 n<1000且m<2000

100%的数据 n<100000且m<200000

 


 

思路:整个程序分为两个部分,前半部分最小生成树,后半部分求最小偏心距。由于题目所说所有边长度不等,那么最小生成树就是唯一的,这个事情可以用克鲁斯卡尔想,没用等长的边,排完序后选择边的方案唯一那么最小生成树也就是唯一的。至于后半部分的求最小偏心据问题,用朴素是n^2代价,很不划算,如果能找到树的直径,那么中心点必然在直径最中间的那条边的其中一个端点上。那么,如果有多条直径,中心点又如何处理呢?

某北大学生给出一个神一样的证明:

 

             

先从1出发搜到最远一点2,再从2出发搜到距2最远一点3,则2到3的路径即为一条直径。
往证“1到2的路径与每条直径必相交”。用反证法
假设直径AB与12路径无焦点
将1设定为树根,则对于12路径上的任意一点X必成立以下推论:
  “X到2的路径是X通向其后代的路径中一条最长的路径”。
设AB上距1最近一点为C
不妨设LEN(AC)>=LEN(cb)
令C与2的最小公共前辈为X,则X必在1到2的路径上,由推论可知:
LEN(2x)>=len(xa)>len(ac)>=len(cb)
由此可知
  len(2a)=len(2x)+len(xa)>=len(xa)+len(xa)>len(ac)+len(cb)=len(ab)
即出现了比直径更长的路径,矛盾。
至此证明了直径AB与1到2的路径存在公共点,设公共点中距1最近的一点为Y,
不妨设LEN(ay)>=len(yb),
由于ab是直径,故 len(ab)>=len(a2)
又由于Y是12路径上的点,由推论可知:
  LEN(2y)>=LEN(YA)>=LEN(YB)
则有
  LEN(2a)=len(2y)+len(ya)>=LEN(AY)+LEN(YB)=LEN(AB)
即 len(ab)=len(a2)
至此证明了2A是直径。

 

由此可以推论出如果有多条直径则必然有公共点或公共边,若为公共点则必为中心点若为公共边则公共边必包含中心边或中心点。然则用树状动规从随便一个点A出发找到一个最远点B,再从这个最远点B找到2最远点C,BC为直径,再找中心点就ok了。

View Code
 1 //最慢数据0.578
 2 #include <fstream>
 3 #include <vector>
 4 using namespace std;
 5 
 6 ifstream cin("minlen.in");
 7 ofstream cout("minlen.out");
 8 int n, m, tt = clock();
 9 
10 struct edge
11 {
12       int  x, y, z;
13 }a[200001];
14 
15 struct node
16 {
17        int p, w;//p是点,w为权 
18 }e;
19 
20 vector <node> h[100001];
21 int p[100001], d[100001], b[100001];//d最大深度,b最大深度的点;
22 
23 bool cmp(const edge &a, const edge &b)
24 {
25      return a.z < b.z;
26 }
27 
28 int search(int a)
29 {
30     if (p[a] == 0)
31        return a;
32     return p[a] = search(p[a]);
33 }
34 
35 void treedp(int c)

36 {
37      int i, j; 
38      d[c] = -1;
39      for (i = 0 ; i < h[c].size(); i++)
40          if (d[j = h[c][i].p] == 0)//不回访 
41          {
42             treedp(j);
43             p[j] = c;
44             if (d[j] + h[c][i].w > d[c])
45             {
46                d[c] = d[j] + h[c][i].w;
47                b[c] = b[j];
48             }
49          }
50      if (d[c] < 0)//叶子 
51      {
52         d[c] = 0; 
53         b[c] = c;
54      } 
55 } 
56 int main()
57 {
58     int i, u, v, j;
59     long long s = 0, ans;
60     cin >> n >> m;
61     for (i = 0; i < m; i++)
62         cin >> a[i].x >> a[i].y >> a[i].z;
63     sort(a, a + m, cmp);
64     for (i = j = 0; j < n - 1; i++)
65     {
66         u = search(a[i].x);
67         v = search(a[i].y);
68         if (u == v)
69            continue;
70         else
71         {
72             p[u] = v;
73             e.p = a[i].x;
74             e.w = a[i].z;
75             s += a[i].z;
76             h[a[i].y].push_back(e);
77             e.p = a[i].y;
78             h[a[i].x].push_back(e);
79             j++;
80         }
81     }
82     treedp(1);
83     v = b[1];
84     memset(d, 0, sizeof(d));
85     treedp(v);
86     u = b[v];
87     i = u;
88     while (d[p[i]] <= d[v] / 2)i = p[i];
89     ans = min(d[v] - d[i], d[p[i]]);
90     cout << s << endl << ans << endl;
91     //cout << clock() - tt;
92     return 0;
93 }

 

posted @ 2012-10-04 23:04  Ka8  阅读(624)  评论(0)    收藏  举报