BZOJ 1912:[Apio2010]patrol 巡逻(树直径)

1912: [Apio2010]patrol 巡逻

Input

第一行包含两个整数 n, K(1 ≤ K ≤ 2)。接下来 n – 1行,每行两个整数 a, b, 表示村庄a与b之间有一条道路(1 ≤ a, b ≤ n)。

Output

输出一个整数,表示新建了K 条道路后能达到的最小巡逻距离。

Sample Input

8 1

1 2

3 1

3 4

5 3

7 5

8 5

5 6

Sample Output

11

HINT

10%的数据中,n ≤ 1000, K = 1; 
30%的数据中,K = 1; 
80%的数据中,每个村庄相邻的村庄数不超过 25; 
90%的数据中,每个村庄相邻的村庄数不超过 150; 
100%的数据中,3 ≤ n ≤ 100,000, 1 ≤ K ≤ 2。

Source

分析:

k=0时显然每条边经过一次,答案为2*(n-1)

k=1时可以发现,新路连的两个端点i,j之间形成环,环上所有边都不需要经过两次,相当于k=0时的答案减去ij间距再+1(新路必须经过),显然ij距离最大时最优,故取树上最长链。

k=2时可以发现,在k=1基础上再连一条边不能直接取次长链,因为每经过k=1选取的链上的边,你将会多走2步,故将原来最长链上的边变成-1,再求最长链,在将k=1答案减去+1即可。

program pat;
type
  point=^node;
   node=record
      x,v:longint; next:point;
   end;
var
  a:array[0..100000]of point;
  f1,f2,s1,s2:array[0..100000]of longint;
  n,i,m,max,k,x,y,ans:longint; p:point;
procedure add(x,y:longint);
var p:point;
begin
  new(p); p^.x:=y; p^.v:=1; p^.next:=a[x]; a[x]:=p;
end;
procedure dfs(x,e:longint);
var p:point; y:longint;
begin
  s1[x]:=x; s2[x]:=x;
  new(p); p:=a[x];
  while p<>nil do
   begin
     y:=p^.x;
     if y=e then begin p:=p^.next; continue; end;
     dfs(y,x);
     if f1[y]+p^.v>f1[x] then
     begin
       f2[x]:=f1[x]; s2[x]:=s1[x];
       f1[x]:=f1[y]+p^.v; s1[x]:=y;
      end
     else
     if f1[y]+p^.v>f2[x] then
     begin
       f2[x]:=f1[y]+p^.v; s2[x]:=y;
     end;
     p:=p^.next;
   end;
  if f1[x]+f2[x]>f1[max]+f2[max] then
   max:=x;
end;
procedure work(x:longint);
var p:point;  y:longint;
begin
  new(p); p:=a[x];
  while p<>nil do
   begin
     y:=p^.x;
     if y=s1[x] then begin p^.v:=-1; work(y); break; end;
     p:=p^.next;
   end;
end;
begin
  assign(input,‘pat.in‘);reset(input);
  assign(output,‘pat.out‘);rewrite(output);
  readln(n,k);
  for i:=1 to n-1 do
   begin
     readln(x,y); add(x,y);  add(y,x);
   end;
  ans:=2*(n-1); max:=0; f1[0]:=0; f2[0]:=0;
  dfs(1,0);
  ans:=ans-f1[max]-f2[max]+1;
  if k=1 then writeln(ans) else
   begin
     fillchar(f1,sizeof(f1),0);
     fillchar(f2,sizeof(f2),0);
     new(p); p:=a[max];
     while p<>nil do
      begin
        y:=p^.x;
        if (y=s1[max])or(y=s2[max]) then p^.v:=-1;
        p:=p^.next;
      end;
     work(s1[max]); work(s2[max]);  max:=0;
     dfs(1,0);
     ans:=ans-f1[max]-f2[max]+1;
     writeln(ans);
   end;
  close(input); close(output);
end.

时间: 2024-11-05 08:26:17

BZOJ 1912:[Apio2010]patrol 巡逻(树直径)的相关文章

bzoj 1912 : [Apio2010]patrol 巡逻 树的直径

题目链接 如果k==1, 显然就是直径. k==2的时候, 把直径的边权变为-1, 然后在求一次直径. 变为-1是因为如果在走一次这条边, 答案会增加1. 学到了新的求直径的方法... #include <bits/stdc++.h> using namespace std; #define pb(x) push_back(x) #define ll long long #define mk(x, y) make_pair(x, y) #define lson l, m, rt<<

bzoj 1912: [Apio2010]patrol 巡逻

呵呵呵呵呵呵,自己画图,大概半个小时,觉的连上边会成环(是不是该交仙人掌了??)然后求环不重合部分最大就好了, 结果写了一坨DP,最后写不下去了,再次扒了题解. 发现我真的是个sb. k==1,直接是直径 k==2,搞出直径然后把直径删掉(把权值赋为-1,再找直径)(有点像我一开始想的每次找个最长链去贪心,然而,,总觉得,这种题贪心这么可能对) 1 /*#include <bits/stdc++.h> 2 #define LL long long 3 #define lowbit(x) x&a

【BZOJ 1912】 [Apio2010]patrol 巡逻

1912: [Apio2010]patrol 巡逻 Time Limit: 4 Sec Memory Limit: 64 MB Submit: 669 Solved: 380 [Submit][Status][Discuss] Description Input 第一行包含两个整数 n, K(1 ≤ K ≤ 2).接下来 n – 1行,每行两个整数 a, b, 表示村庄a与b之间有一条道路(1 ≤ a, b ≤ n). Output 输出一个整数,表示新建了K 条道路后能达到的最小巡逻距离. S

【BZOJ-1912】patrol巡逻 树的直径 + DFS(树形DP)

1912: [Apio2010]patrol 巡逻 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 1034  Solved: 562[Submit][Status][Discuss] Description Input 第一行包含两个整数 n, K(1 ≤ K ≤ 2).接下来 n – 1行,每行两个整数 a, b, 表示村庄a与b之间有一条道路(1 ≤ a, b ≤ n). Output 输出一个整数,表示新建了K 条道路后能达到的最小巡逻距离.

bzoj 1912 巡逻(树直径)

Description Input 第一行包含两个整数 n, K(1 ≤ K ≤ 2).接下来 n – 1行,每行两个整数 a, b, 表示村庄a与b之间有一条道路(1 ≤ a, b ≤ n). Output 输出一个整数,表示新建了K 条道路后能达到的最小巡逻距离. Sample Input 8 1 1 2 3 1 3 4 5 3 7 5 8 5 5 6 Sample Output 11 HINT 10%的数据中,n ≤ 1000, K = 1: 30%的数据中,K = 1: 80%的数据中,

【bzoj1912】 Apio2010—patrol 巡逻

http://www.lydsy.com/JudgeOnline/problem.php?id=1912 (题目链接) 题意 给出一棵树,要求在树上添加K(1 or 2)条边,添加的边必须经过一次,使得从1号节点到达每个节点最后返回1号节点所经过的路径最短. Solution 如果不添加边,那么答案一定是每条边经过两次. 如果K为1,那么答案就很明显对吧,找到树的直径,链接直径两端点,使得直径上的边只经过一次,答案最优. 那么如果K为2,我们会发现,当两个环有变重叠时,重叠的边同样是要经过2次.

P1912: [Apio2010]patrol 巡逻

这道题讨论了好久,一直想不明白,如果按传统的随便某一个点出发找最长链,再回头,K=2 的时候赋了-1就没法用这种方法找最长链了,于是乎,更强的找最长链的方法就来了..类似于DP的东西吧.先上代码: 1 const maxn=100002; 2 type 3 node=record 4 f,t,l:longint; 5 end; 6 var n,k,i,j,ans,num,f,t,diameter,s,sum:longint; 7 b:array[0..2*maxn] of node; 8 hea

BZOJ1912 [Apio2010]patrol 巡逻

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000作者博客:http://www.cnblogs.com/ljh2000-jump/转载请注明出处,侵权必究,保留最终解释权! Description Input 第一行包含两个整数 n, K(1 ≤ K ≤ 2).接下来 n – 1行,每行两个整数 a, b, 表示村庄a与b之间有一条道路(1 ≤ a, b ≤ n). Output 输出一个整数,表示新建了K 条道路后能达到的最小

【BZOJ】【1912】【APIO2010】patrol巡逻

树形DP 说是树形DP,其实就是求树的最长链嘛…… K=1的时候明显是将树的最长链的两端连起来最优. 但是K=2的时候怎么搞? 考虑第一次找完树的最长链以后的影响:第一次找过的边如果第二次再走,对答案的贡献会变成-1,因为两次都选这一段的话,反而会使得这一段不得不走两次(如果只被选一次的话就可以只走一次),所以就将第一次找出的树的最长链上的边权值都改为-1.这个操作可以用链表实现(类比一下最小费用最大流的spfa实现!) 题解:http://blog.csdn.net/qpswwww/artic