[BZOJ 1912][Apio2010]patrol 巡逻:树的直径

点击这里查看原题

这个题k只有两种情况,因此可以分类来考虑。
k=1时显然将树的直径两端连起来可以省下的路径最多。
k=2时,再建一条路一定会形成一个新的环,因为建的路必须走,所以环上每一条边都需要走一次。环上的是树的直径的边越多,则损失越大(因为重复走了),环上的不是树的直径的边越多,则节省的路径越多。因此需要将树的直径上的每一条边权变为-1,然后再做一次树的直径。

/*
User:Small
Language:C++
Problem No.:1912
*/
#include<bits/stdc++.h>
#define ll long long
#define inf 999999999
using namespace std;
const int M=1e5+5;
int n,m,ans,fir[M],tot=1,s,d,son1[M],son2[M];
struct edge{
    int v,w,nex;
}e[M<<2];
void add(int u,int v,int w){
    e[++tot]=(edge){v,w,fir[u]};
    fir[u]=tot;
}
int dfs(int u,int fa){
    int max1=0,max2=0;
    for(int i=fir[u];i;i=e[i].nex){
        int v=e[i].v;
        if(v==fa) continue;
        int nowh=dfs(v,u)+e[i].w;
        if(nowh>max1){
            max2=max1;
            son2[u]=son1[u];
            son1[u]=i;
            max1=nowh;
        }
        else if(nowh>max2){
            son2[u]=i;
            max2=nowh;
        }
    }
    if(max1+max2>d){
        d=max1+max2;
        s=u;
    }
    return max1;
}
int main(){
    freopen("data.in","r",stdin);//
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v,1);
        add(v,u,1);
    }
    ans=(n-1)<<1;
    dfs(1,0);
    ans-=d-1;
    if(m>1){
        for(int i=son1[s];i;i=son1[e[i].v]) e[i].w=e[i^1].w=-1;
        for(int i=son2[s];i;i=son1[e[i].v]) e[i].w=e[i^1].w=-1;
        memset(son1,0,sizeof(son1));
        memset(son2,0,sizeof(son2));
        d=0;
        dfs(1,0);
        ans-=d-1;
    }
    printf("%d\n",ans);
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值