[BZOJ 1912][APIO 2010]patrol 巡逻(树的直径)

142 篇文章 0 订阅
41 篇文章 0 订阅

题目链接

http://www.lydsy.com/JudgeOnline/problem.php?id=1912

题目大意

给一棵树,求加 k 条边之后,从1号点遍历每个点之后再回到1号点的最小距离和。k=12

思路

如果不加边,那么遍历走过的距离显然是 2(n1) 。注意到一个很有意思的限制: k=12 。那么可以分类讨论以下两种情况
1、 k=1 ,很显然最优的方法就是在树的最长路径 xy 之间连一条边,这样 x y之间的距离缩短为1,这个最长路径就是树的直径,我们可以DFS求一遍树的直径 len1 ,那么最终答案为 2(n1)len1+1
另外为了情况2的方便,要在DFS过程中用链表记录下树的直径的路径。这个路径是两条路径相交而成的,假设这两条路径的交点为 x :一条是x下面的最长路,另一条是 x 下面的次长路(注意它也是包含了y下面的最长路),如下图
这里写图片描述
除非次长路为0(x下面是一条链),否则直径一定是这样的形态

2、 k=2 ,肯定是找树中次长路径 xy ,给 xy 之间连边,我们在情况1的基础上,将第一次DFS求得的树的直径上的所有边边权全部标为-1,再DFS一次,求出树中的次长路径 len2 ,最终答案为 2(n1)len1+1len2+1

代码

注意情况2中标记路径的过程,分两次做,一次是修改x下面最长路的路径,一次是修改x下面的次长路的路径,也是y下面的最长路路径。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXE 100010
#define MAXV 100010

using namespace std;

int n,m;

struct edge
{
    int u,v,w,next;
}edges[2*MAXE];

int head[MAXV],nCount=1;

void AddEdge(int U,int V,int W)
{
    edges[++nCount].u=U;
    edges[nCount].v=V;
    edges[nCount].w=W;
    edges[nCount].next=head[U];
    head[U]=nCount;
}

int diameter,s; //树的直径为diameter,直径的起点是s
int son1[MAXV],son2[MAXV]; //记录最长路与次长路的路径

int DFS(int u,int fa)
{
    int max1=0,max2=0; //最长路与次长路
    for(int p=head[u];p!=-1;p=edges[p].next)
    {
        int v=edges[p].v;
        if(v==fa) continue;
        int nowh=DFS(v,u)+edges[p].w;
        if(nowh>max1) max2=max1,son2[u]=son1[u],max1=nowh,son1[u]=p;
        else if(nowh>max2) max2=nowh,son2[u]=p;
    }
    if(diameter<max1+max2) diameter=max1+max2,s=u;
    return max1;
}

int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        AddEdge(u,v,1);
        AddEdge(v,u,1);
    }
    int ans=(n-1)<<1;
    diameter=0;
    memset(son1,-1,sizeof(son1));
    memset(son2,-1,sizeof(son2));
    DFS(1,-1);
    ans-=diameter-1;
    if(m>1)
    {
        diameter=0;
        for(int i=son1[s];i!=-1;i=son1[edges[i].v]) edges[i].w=edges[i^1].w=-1;
        for(int i=son2[s];i!=-1;i=son1[edges[i].v]) edges[i].w=edges[i^1].w=-1;
        memset(son1,-1,sizeof(son1));
        memset(son2,-1,sizeof(son2));
        DFS(1,-1);
        ans-=diameter-1;
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值