bzoj1912——树的最长链

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<bits/stdc++.h>
using namespace std;
int read(){
    char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
    while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
int head[100005],s1[100005],s2[100005];
int n,k,cnt=1,dis,mx,tot;
struct node{
    int to,val,nxt;
}L[200005];
void add(int x,int y){
    L[++cnt].to=y;L[cnt].val=1;L[cnt].nxt=head[x];head[x]=cnt;
    L[++cnt].to=x;L[cnt].val=1;L[cnt].nxt=head[y];head[y]=cnt;
}
int dfs(int x,int fa){
    int mx1=0,mx2=0;
    for(int i=head[x];i;i=L[i].nxt){
        int to=L[i].to;
        if(to==fa) continue;
        int v=L[i].val+dfs(to,x);
        if(v>mx1) mx2=mx1,mx1=v,s2[x]=s1[x],s1[x]=i;
        else if(v>mx2) mx2=v,s2[x]=i;
    }
    if(mx1+mx2>dis) dis=mx1+mx2,mx=x;
    return mx1;
}
int main()
{
    n=read();k=read();tot=(n-1)*2;
    for(int i=1;i<=n-1;i++){
        int x=read(),y=read();
        add(x,y);
    }
    dfs(1,0);tot=tot-dis+1;
    if(k==2){
        dis=0;
        for(int i=s1[mx];i;i=s1[L[i].to]) L[i].val=L[i^1].val=-1;
        for(int i=s2[mx];i;i=s1[L[i].to]) L[i].val=L[i^1].val=-1;
        dfs(1,0);tot=tot-dis+1;
    }
    printf("%d\n",tot);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值