bzoj 1912: [Apio2010]patrol 巡逻

1912: [Apio2010]patrol 巡逻

Time Limit: 4 Sec   Memory Limit: 64 MB
Submit: 1007   Solved: 549
[ Submit][ Status][ Discuss]

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%的数据中,每个村庄相邻的村庄数不超过 25; 
90%的数据中,每个村庄相邻的村庄数不超过 150; 
100%的数据中,3 ≤ n ≤ 100,000, 1 ≤ K ≤ 2。

Source

[ Submit][ Status][ Discuss]

题解: 

先说明一个定义: 树的直径是一棵树上最长的一条路径,从树上任意一个点dfs出离他最远的一个点,再从这个点dfs出离他最远的点,两次dfs出的点之间的路径就是树上最长的链,即最长的一条路径。

k==1  因为要使走的重复的路尽可能的短,所以我们在添加一条边的时候就要保证形成的环中的边尽可能的多,也就是求树上的最长链。ans=2(n-1)-len1+1

k==2 这时候需要在1的基础上再求最长链,因为如果再形成的环与第一个环有重叠的话,那么重叠的部分还是会走两次,所以把第一遍走过的路径付成-1,再求树的直径即可。ans=2(n-1)-len1+1-len2+1

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 200003
using namespace std;
int n,k;
int point[N],next[N],v[N],c[N],tot=-1,ans,ansx;
int last[N],len1,len2;
void add(int x,int y,int z)
{
	tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
	tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
}
void dfs(int x,int fa,int dis,int num)
{
	if (dis>ans)
	{
	 ans=dis,ansx=x;
    }
	for (int i=point[x];i!=-1;i=next[i])
	 if (fa!=v[i])
	  {
	    last[v[i]]=i;
	  	dfs(v[i],x,dis+c[i],i);
	  }
}
void change(int s,int t)
{
	int now=t;
	while (now!=s)
	 {
	 	c[last[now]]=-1;
	 	c[last[now]^1]=-1;
	 	now=v[last[now]^1];
	 }
}
int dp(int x,int fa)
{
	int first=0; int second=0;
	for (int i=point[x];i!=-1;i=next[i])
	 if (v[i]!=fa)
	  {
	  	int t=c[i]+dp(v[i],x);
	  	if (t>first)
	  	 {
	  	 	second=first; first=t;
	  	 }
	  	else
	  	 if (t>second)
	  	  second=t;
	  }
	if (first+second>ans)
	 ans=first+second;
	return first;
}
int main()
{
	scanf("%d%d",&n,&k);
	tot=-1;
	memset(point,-1,sizeof(point));
	memset(next,-1,sizeof(next));
	for (int i=1;i<n;i++)
	 {
	 	int x,y; scanf("%d%d",&x,&y);
	 	add(x,y,1);
	 }
	dfs(1,0,0,0); 
	int head=ansx;
	ans=0; 
	dfs(ansx,0,0,0);
	int tail=ansx;   
	len1=ans; 
	change(head,tail);
	if (k==1)
	 {
	 	printf("%d\n",2*(n-1)-ans+1);
	 	return 0;
	 }
	ans=0;
	dp(1,0);
	printf("%d\n",2*(n-1)-len1-ans+2);
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值