BZOJ1912 [Apio2010]patrol 巡逻
Posted ljh2000
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 条道路后能达到的最小巡逻距离。
Sample Input
8 1
1 2
3 1
3 4
5 3
7 5
8 5
5 6
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。
正解:树形DP
解题报告:
这题很有意思,我记得以前做过一道叫做巡访的题目,正好是k=1的情况,结果这道题叫巡逻XD
考虑k=1的时候,我们的答案很容易想到就是2*(n-1)-最长链+1,因为如果能加一条边的话,因为我希望减少的尽可能多,那么我只需要把最长链的首尾接起来,就不需要来回走,加一就是加了这一条新加入的边。
但是k=2的时候呢?首先还是往最长链上面思考。然而做k=1的时候已经用掉了一段,k=2的时候怎么知道和k=1不重叠呢?
很简单,我们在做k=1之后把最长链上的边权全部修改为-1,再跑一遍最长链就可以了。可能有人会疑问,那-1的边又被选了那不是相当于还是选进去两次了吗?但是考虑第一次算这条边的时候加了一,第二次的时候加的是-1,相当于是这条边没有产生任何贡献。可以画一画图就会发现,相当于是把两条交错的链变成了两条分开的链。这个做法很优秀。
所以最后的总复杂度就是O(n)。
1 //It is made by ljh2000 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <algorithm> 8 #include <ctime> 9 #include <vector> 10 #include <queue> 11 #include <map> 12 #include <set> 13 using namespace std; 14 typedef long long LL; 15 const int inf = (1<<30); 16 const int MAXN = 100011; 17 const int MAXM = 200011; 18 int n,k,ecnt,next[MAXM],to[MAXM],w[MAXM]; 19 int f[MAXN][2],first[MAXN],g[MAXN],p[MAXN]; 20 int ans,root,Ans,Son,Son2; 21 22 inline int getint() 23 { 24 int w=0,q=0; char c=getchar(); 25 while((c<‘0‘ || c>‘9‘) && c!=‘-‘) c=getchar(); if(c==‘-‘) q=1,c=getchar(); 26 while (c>=‘0‘ && c<=‘9‘) w=w*10+c-‘0‘, c=getchar(); return q ? -w : w; 27 } 28 inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; w[ecnt]=1; } 29 inline void dfs(int x,int fa){ 30 int now,son=0,son2=0; 31 for(int i=first[x];i;i=next[i]) { 32 int v=to[i]; if(v==fa) continue; dfs(v,x); now=f[v][0]+w[i]; 33 if(now>f[x][0]) son=g[x],son2=p[x],f[x][1]=f[x][0],f[x][0]=now,g[x]=v,p[x]=i; else if(now>f[x][1]) f[x][1]=now,son=v,son2=i; 34 } 35 if(f[x][0]+f[x][1]>ans) { ans=f[x][0]+f[x][1]; root=x; Son=son; Son2=son2; } 36 } 37 38 inline void work(){ 39 n=getint(); k=getint(); int x,y; for(int i=1;i<n;i++) { x=getint(); y=getint(); link(x,y); link(y,x); } 40 dfs(1,0); Ans=2*(n-1)-ans+1; if(k==1) { printf("%d",Ans); return ; } 41 if(f[root][1]>0) { x=Son; w[Son2]=-1; while(g[x]) { w[p[x]]=-1; x=g[x]; } } 42 x=root; while(g[x]) w[p[x]]=-1,x=g[x]; ans=0; memset(f,0,sizeof(f)); 43 dfs(1,0); Ans-=ans-1; printf("%d",Ans); 44 } 45 46 int main() 47 { 48 work(); 49 return 0; 50 }
以上是关于BZOJ1912 [Apio2010]patrol 巡逻的主要内容,如果未能解决你的问题,请参考以下文章
bzoj 1912 : [Apio2010]patrol 巡逻 树的直径