APIO2010 巡逻
【题目描述】
在一个地区中有 n个村庄,编号为1, 2, ..., n。有n – 1条道路连接着这些村庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通过这些道路到达其他任一个村庄。每条道路的长度均为 1个单位。
为保证该地区的安全,巡警车每天要到所有的道路上巡逻。警察局设在编号为1的村庄里,每天巡警车总是从警察局出发,最终又回到警察局。
下图表示一个有8个村庄的地区,其中村庄用圆表示(其中村庄 1用黑色的圆表示),道路是连接这些圆的线段。为了遍历所有的道路,巡警车需要走的距离为14 个单位,每条道路都需要经过两次。
为了减少总的巡逻距离,该地区准备在这些村庄之间建立 K 条新的道路,每条新道路可以连接任意两个村庄。两条新道路可以在同一个村庄会合或结束(见下面的图例(c) )。一条新道路甚至可以是一个环,即,其两端连接到同一个村庄。
由于资金有限,K 只能是 1或2。同时,为了不浪费资金,每天巡警车必须经过新建的道路正好一次。
下图给出了一些建立新道路的例子:
在(a)中,新建了一条道路,总的距离是 11。在(b)中,新建了两条道路,总的巡逻距离是 10。在(c)中,新建了两条道路,但由于巡警车要经过每条新道路正好一次,总的距离变为了 15。
试编写一个程序,读取村庄间道路的信息和需要新建的道路数,计算出最佳的新建道路的方案使得总的巡逻距离最小,并输出这个最小的巡逻距离。
【输入文件】
第一行包含两个整数 n, K(1 ≤ K ≤ 2)。接下来 n – 1行,每行两个整数 a, b,
表示村庄a与b之间有一条道路(1 ≤ a, b ≤ n)。
【输出文件】
输出一个整数,表示新建了K 条道路后能达到的最小巡逻距离。
【输入样例】
8 1
1 2
3 1
3 4
5 3
7 5
8 5
5 6
【输出样例】
11
【数据范围】
10%的数据中,n ≤ 1000, K = 1;
30%的数据中,K = 1;
80%的数据中,每个村庄相邻的村庄数不超过 25;
90%的数据中,每个村庄相邻的村庄数不超过 150;
100%的数据中,3 ≤ n ≤ 100,000, 1 ≤ K ≤ 2。
【题目分析】
K=1的情况比较简单,求出树的直径,把直径两端连上就行了。
K=2的时候,还要练一条边,这时候,我们讨论3种情况:
1、所连的两个端点在直径上某一个点的不同非直径子树上
2、所连的两个端点在直径上某两个点的非直径子树上
3、所连的两个端点在直径上某一个点的相同非直径子树上
首先对于情况一,可以对直径上每个点进行一次dfs求得不重合的最长链与次长链解决。
对于情况三,也可以在dfs的过程中解决,我们主要讨论一下情况二。
把直径作为一条长度为tot的链,用max[i]表示第i个点可以扩展的非直径最长链,那么我们的目的是求max[j]+max[i]-(j-i)的最大值,其中j>i。
将上式转化,可化为max[i]+i+max[j]-j,那么对于每一个枚举的i,max[i]+i为定值,而对于j=i+1..n中max[j]-j的最大值也可以由预处理得到,这样就得出了结果。
最后的答案在这三种情况中取一个min就行了。
【代码实现】


1 program test2; 2 type bian=record 3 y,next:longint; 4 end; 5 var a:array[0..200000]of bian; 6 d,b,zj,pre,g,len,q:array[0..100000]of longint; 7 v:array[0..100000]of boolean; 8 i,j,m,n,ans1,ans2,m1,m2,tot,x,y,k,tmp:longint; 9 procedure bfs(s:longint); 10 var t,w,i,j,x:longint; 11 begin 12 fillchar(v,sizeof(v),0); 13 t:=1;w:=1;d[1]:=s;v[s]:=true;b[s]:=0; 14 while t<=w do 15 begin 16 x:=d[t];inc(t); 17 i:=g[x]; 18 while i<>0 do 19 begin 20 if not v[a[i].y] then 21 begin 22 pre[a[i].y]:=x; 23 b[a[i].y]:=b[x]+1; 24 inc(w); 25 d[w]:=a[i].y; 26 v[a[i].y]:=true; 27 end; 28 i:=a[i].next; 29 end; 30 end; 31 end; 32 function dfs(i:longint):longint; 33 var tmp,tt,j,m1,m2:longint; 34 begin 35 tmp:=-1; 36 m1:=0;m2:=0; 37 j:=g[i]; 38 while j<>0 do 39 begin 40 if not v[a[j].y] then 41 begin 42 v[a[j].y]:=true; 43 tt:=dfs(a[j].y); 44 if tt>tmp then tmp:=tt; 45 if tt+1>m1 then 46 begin 47 m2:=m1; 48 m1:=tt+1; 49 end 50 else if tt+1>m2 then m2:=tt+1; 51 end; 52 j:=a[j].next; 53 end; 54 if ans1-m1-m2+1<ans2 then ans2:=ans1-m1-m2+1; 55 exit(tmp+1); 56 end; 57 procedure insert(x,y:longint); 58 begin 59 inc(tot); 60 a[tot].y:=y; 61 a[tot].next:=g[x]; 62 g[x]:=tot; 63 end; 64 function max(a,b:longint):longint; 65 begin 66 if a>b then exit(a) 67 else exit(b); 68 end; 69 begin 70 readln(n,m); 71 for i:=1 to n-1 do 72 begin 73 readln(x,y); 74 insert(x,y); 75 insert(y,x); 76 end; 77 bfs(1); 78 j:=1; 79 for i:=1 to n do 80 if b[i]>b[j] then j:=i; 81 bfs(j); 82 k:=j; 83 for i:=1 to n do 84 if b[i]>b[k] then k:=i; 85 ans1:=2*n-2-b[k]+1; 86 if m=1 then 87 begin 88 writeln(ans1); 89 close(input);close(output); 90 halt; 91 end; 92 tot:=1;zj[tot]:=k; 93 fillchar(v,sizeof(v),0); 94 v[k]:=true; 95 while k<>j do 96 begin 97 k:=pre[k]; 98 inc(tot); 99 zj[tot]:=k; 100 v[k]:=true; 101 end; 102 ans2:=maxlongint; 103 for i:=1 to tot do 104 begin 105 m1:=0;m2:=0; 106 b[zj[i]]:=0; 107 j:=g[zj[i]]; 108 while j<>0 do 109 begin 110 if not v[a[j].y] then 111 begin 112 v[a[j].y]:=true; 113 tmp:=dfs(a[j].y)+1; 114 if tmp>m1 then 115 begin 116 m2:=m1; 117 m1:=tmp; 118 end 119 else if tmp>m2 then m2:=tmp; 120 end; 121 j:=a[j].next; 122 end; 123 len[i]:=m1; 124 if ans1-m1-m2+1<ans2 then 125 ans2:=ans1-m1-m2+1; 126 end; 127 q[tot]:=len[tot]-tot; 128 for i:=tot-1 downto 1 do 129 q[i]:=max(q[i+1],len[i]-i); 130 for i:=1 to tot-1 do 131 begin 132 tmp:=q[i+1]+len[i]+i; 133 if ans1-tmp+1<ans2 then ans2:=ans1-tmp+1; 134 end; 135 writeln(ans2); 136 end.