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 head,go1,go2:array[0..maxn] of longint;
 9 procedure swap(var a,b:longint);
10 var tem:longint;
11 begin
12   tem:=a; a:=b; b:=tem;
13 end;
14 procedure insert(num,f,t:longint);
15 begin
16   b[num].f:=head[f];
17   b[num].t:=t;
18   b[num].l:=1;
19   head[f]:=num;
20 end;
21 function dfs(x,f:longint):longint;
22 var nowe,max1,max2,tem:longint;
23 begin
24   max1:=0; max2:=0; //tem:=0;
25   nowe:=head[x];
26   while nowe<>0 do
27     begin
28       if b[nowe].t=f then
29         begin
30           nowe:=b[nowe].f;
31           continue;
32         end;
33       tem:=dfs(b[nowe].t,x)+b[nowe].l;
34       if tem>max1 then begin
35         go2[x]:=go1[x];
36         go1[x]:=nowe;
37         max2:=max1;
38         max1:=tem;
39       end
40       else if tem>max2 then
41         begin
42           go2[x]:=nowe;
43           max2:=tem;
44         end;
45       nowe:=b[nowe].f;
46     end;
47   if diameter<max1+max2 then
48     begin
49       diameter:=max1+max2;
50       s:=x;
51     end;
52   exit(max1);
53 end;
54 begin
55   readln(n,k);
56   for i:=1 to n-1 do
57     begin
58       readln(f,t);
59       insert(i*2-1,f,t);
60       insert(i*2,t,f);
61     end;
62   ans:=2*(n-1);
63   diameter:=0;
64   sum:=dfs(1,0);
65   ans:=ans-diameter+1;
66   if k>1 then
67     begin
68       diameter:=0;
69       i:=go1[s];
70       while i<>0 do
71         begin
72           b[i].l:=-1;
73           i:=go1[b[i].t];
74         end;
75       i:=go2[s];
76       while i<>0 do
77         begin
78           b[i].l:=-1;
79           i:=go1[b[i].t];
80         end;
81       t:=dfs(1,0);
82       ans:=ans-diameter+1;
83     end;
84   writeln(ans);
85 end.

 1 int diameter,s; //树的直径为diameter,直径的起点是s
 2 int son1[MAXV],son2[MAXV]; //记录最长路与次长路的路径
 3
 4 int DFS(int u,int fa)
 5 {
 6     int max1=0,max2=0; //与当前点相连的最长路与次长路 之和为不过fa的最长链,或者与fa相连并                       //加上与fa相连边权值,作为连接fa可能的max1 或 max2
 7     for(int p=head[u];p!=-1;p=edges[p].next)
 8     {
 9         int v=edges[p].v;
10         if(v==fa) continue; //一直往下走,直到叶子节点
11         int nowh=DFS(v,u)+edges[p].w;
12         if(nowh>max1) max2=max1,son2[u]=son1[u],max1=nowh,son1[u]=p;
13         else if(nowh>max2) max2=nowh,son2[u]=p;
14     }
15     if(diameter<max1+max2) diameter=max1+max2,s=u;
16     return max1;
17 }

而复杂度也是 O(n)的。

(转载请注明出处:http://www.cnblogs.com/Kalenda/)

时间: 2024-10-05 21:21:17

P1912: [Apio2010]patrol 巡逻的相关文章

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

【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

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 条道路后能达到的最小

【bzoj1912】 Apio2010—patrol 巡逻

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

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巡逻

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

【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 条道路后能达到的最小巡逻距离.

【APIO2010】巡逻

这是一道关于树的直径的好题,值得一刷. 本题有两个难点,一个是分类讨论k,另一个是代码的实现(其实还好). 本题k可以为1或2,因此我们分类讨论一下. 当k=1时,我们可以任选两个点连接,假设我们一条边都不连接,那么我们需要走2*m次,其中m为边的数量.假设我们在x,y上连一条边,那么我们用1个距离节省了dis(x,y)个距离,为了使答案最小化,我们要使dis(x,y)最大,显然我们求一遍树的直径即可,那么答案为2*m-zhijing+1. 当k=2时,就是在k=1的基础上再加上一条边,同样我们