[BZOJ1912] [Apio2010]patrol 巡逻

题目描述

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。

题目分析

当k=1 明显选择一条直径的两个端点来修建道路
这时形成了一个环
当k=2时
假设建的第二条道路形成的环与第一个环没有重叠 那么和第一问的计算方式一样
假设建的第二条道路形成的环与第一个环有重叠 那么只能多经过一次重叠部分 才能经过修建的第二条道路 相当于重叠部分对最终答案没有贡献
那么 我们把第一次选出的直径上的边权设为-1 再求一遍直径即可

#include <cstdio>
#include <cstring>
#include <set>
#include <map>
#include <vector>
#include <cmath>
#include <queue>
#include <algorithm>
using namespace std;
int n,k;
int tot=1;
int head[100100],net[200200],to[200200],val[200200];
void add(int x,int y,int c)
{
    net[++tot]=head[x],head[x]=tot,to[tot]=y,val[tot]=c;
}
int s1[100100],s2[100100];
int num,ans;
int dfs(int x,int temp)
{
    int mx1=0,mx2=0;
    for(int i=head[x];i;i=net[i])
    {
        if(to[i]==temp) continue;
        int tmp=val[i]+dfs(to[i],x);
        if(tmp>mx1) mx2=mx1,mx1=tmp,s2[x]=s1[x],s1[x]=i;
        else if(tmp>mx2) mx2=tmp,s2[x]=i;
    }
    if(mx1+mx2>ans) ans=(mx1+mx2),num=x;
    return mx1;
}
int main()
{   
    scanf("%d%d",&n,&k);
    for(int x,y,i=1;i<n;i++) 
        scanf("%d%d",&x,&y),add(x,y,1),add(y,x,1);
    dfs(1,0);
    if(k==1) printf("%d",2*(n-1)-ans+1);
    else
    {
        for(int i=s1[num];i;i=s1[to[i]]) val[i]=-1,val[i^1]=-1;
        for(int i=s2[num];i;i=s1[to[i]]) val[i]=-1,val[i^1]=-1;
        int sum=2*(n-1)-ans+1;
        ans=0;
        dfs(1,0);
        sum=sum-ans+1;
        printf("%d",sum);
    }
    return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注