【bzoj1912】 Apio2010—patrol 巡逻

http://www.lydsy.com/JudgeOnline/problem.php?id=1912 (题目链接)

题意

  给出一棵树,要求在树上添加K(1 or 2)条边,添加的边必须经过一次,使得从1号节点到达每个节点最后返回1号节点所经过的路径最短。

Solution

  如果不添加边,那么答案一定是每条边经过两次。

  如果K为1,那么答案就很明显对吧,找到树的直径,链接直径两端点,使得直径上的边只经过一次,答案最优。

  那么如果K为2,我们会发现,当两个环有变重叠时,重叠的边同样是要经过2次。也就是说第一次添加边时构成的环上面的边,如果在第二次添加是仍在环上,那么并不会对答案有贡献。所以我们第二次添加边时,先将树的直径上的边的边权改为-1,之后跑一遍树上最长链(注意有负权,所以不能找树的直径)即可。

代码

// bzoj1912
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define LL long long
#define inf 2147483640
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;

const int maxn=100010;
struct edge {int to,next,w;}e[maxn<<1];
int head[maxn],deep[maxn],rt[2],f[maxn][2],son[maxn];
int n,k,cnt,p;

void link(int u,int v) {
	e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;e[cnt].w=1;
	e[++cnt].to=u;e[cnt].next=head[v];head[v]=cnt;e[cnt].w=1;
}
void dfs(int x,int fa) {
	if (deep[rt[p]]<deep[x]) rt[p]=x;
	for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa) {
			deep[e[i].to]=deep[x]+e[i].w;
			dfs(e[i].to,x);
		}
}
int work(int x,int fa) {
	if (x==rt[1]) return 1;
	for (int i=head[x];i;i=e[i].next)
		if (e[i].to!=fa) if (work(e[i].to,x)) {
				e[i].w=e[i&1?i+1:i-1].w=-1;
				return 1;
			}
	return 0;
}
void dd(int x,int fa) {
	for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa) {
			dd(e[i].to,x);
			if (f[x][1]<f[e[i].to][1]+e[i].w) f[x][1]=f[e[i].to][1]+e[i].w,son[x]=e[i].to;
			else f[x][0]=max(f[x][0],f[e[i].to][1]+e[i].w);
		}
	if (f[x][0]==-inf) f[x][0]=0;if (f[x][1]==-inf ) f[x][1]=0;
}
void dp(int x,int fa,int d) {
	if (f[x][1]<d) f[x][0]=f[x][1],f[x][1]=d,son[x]=0;
	else f[x][0]=max(f[x][0],d);
	for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa) {
			if (son[x]==e[i].to) dp(e[i].to,x,e[i].w+f[x][0]);
			else dp(e[i].to,x,e[i].w+f[x][1]);
		}
}
int main() {
	scanf("%d%d",&n,&k);
	for (int u,v,i=1;i<n;i++) {
		scanf("%d%d",&u,&v);
		link(u,v);
	}
	dfs(1,0);deep[rt[p++]]=0;dfs(rt[0],0);
	int ans=(n-1)*2-deep[rt[1]];
	if (k==1) {printf("%d",ans+k);return 0;}
	work(rt[0],0);
	for (int i=1;i<=n;i++) f[i][0]=f[i][1]=-inf;
	deep[1]=0;dd(1,0);
	dp(1,0,-inf);
	int tmp=0;
	for (int i=1;i<=n;i++) tmp=max(tmp,f[i][1]);
	printf("%d",ans-tmp+k);
	return 0;
}

  

时间: 2024-10-18 10:33:35

【bzoj1912】 Apio2010—patrol 巡逻的相关文章

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 巡逻(树直径)

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

【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 : [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

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

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

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

BZOJ1912 APIO2010 洛谷P3629 巡逻

Description: 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通过这些道路到达其 他任一个村庄.每条道路的长度均为 1 个单位. 为保证该地区的安全,巡警车每天要到所有的道路上巡逻.警察局设在编号 为 1 的村庄里,每天巡警车总是从警察局出发,最终又回到警察局. 下图表示一个有 8 个村庄的地区,其中村庄用圆表示(其中村庄 1 用黑色的 圆表示),道路是连接这些圆的线段.为了遍历所有