APIO2010巡逻(树上带权直径)

题目链接:https://www.luogu.org/problem/show?pid=3629

 

题解:

看到这题题解一片空白,身为蒟蒻的我也想为社会做点贡献……

首先要知道:

1.假如不加边,每条边都要走两次。

2.假如加了一条边,那么会形成一个环,而且环上的边只需要走一次,其余的边要走两次。

(自己yy以下就可以知道了)

对于k=1的话,我们就要使环上的边尽量多,也就是说我们要找树的直径,使得树的直径在环内。

而对于k=2的话,再加一条边的时候,会再多一个环。

这时我们要知道:

1.如果一条边仅在第二个环出现过,只用走一次。

2.如果一条边在两个环都出现过,要走两次。

3.如果一条边在两个环都没出现过,要走两次。

(自己yy以下就可以知道了)

所以我们给第一个环上的边-1的权值,其余边给1的权值,求出树上权值最大的一条路径(假直径O(∩_∩)O)

然后问题就迎刃而解了。

附上蒟蒻的代码,我的100来行,同一机房神犇的代码60+行

#include<iostream>
#include<fstream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
using namespace std;
int n,k,root1,root2,cir,cir2;
int cur,head[100010],d[100010],heavy[100010],w[100010];
struct tedge
{
    int to,nex;
}e[200010];
struct data
{
    int e,v;
}son[100010];
void Add(int u,int v)
{
    cur++;
    e[cur].to = v;
    e[cur].nex = head[u];
    head[u] = cur;
}
bool cmp(data a,data b)
{
    return a.e>b.e;
}
void dfs(int u,int f)
{
    int cnts=0;
    for (int i=head[u]; i!=-1; i=e[i].nex)
    {
        int v=e[i].to;
        if (v==f) continue;
        dfs(v,u);
        if (d[u]<d[v]+w[v]) 
        {
            d[u] = d[v]+w[v];
            heavy[u] = v;
        }
    }
    for (int i=head[u]; i!=-1; i=e[i].nex)
    {
        int v=e[i].to;
        if (v==f) continue;
        cnts++;  son[cnts].e = d[v]+w[v];  son[cnts].v = v;
    }
    sort(son+1,son+1+cnts,cmp);
    if (cnts==0) d[u] = 0;
    if (cnts>=1&&cir<son[1].e) 
    {
        cir = son[1].e;
        root1 = son[1].v;
    }
    if (cnts>=2&&cir<son[1].e+son[2].e)
    {
        cir = son[1].e+son[2].e;
        root1 = son[1].v;  root2 = son[2].v;
    }
}
void Change()
{
    int u = root1;
    while (u!=0)
    {
        w[u] = -1;  u = heavy[u];
    }
    u = root2;
    while (u!=0)
    {
        w[u] = -1;  u = heavy[u];
    }
}
int main()
{
    scanf("%d%d",&n,&k);
    for (int i=0; i<=n; i++)
    head[i] = -1;
    for (int i=2; i<=n; i++)
    w[i] = 1;
    for (int i=1; i<n; i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        Add(x,y);  Add(y,x);
    }
    cir = 0;
    dfs(1,0);
    if (k==1)
    {
        printf("%d\n",2*(n-1)-cir+1);
        return 0;
    }
    Change();
    cir2 = cir;  cir = -1e9;
    for (int i=1; i<=n; i++)
    d[i] = -1e9;
    dfs(1,0);
    printf("%d\n",2*(n-1)-cir-cir2+2);
    return 0;
}

 

转载于:https://www.cnblogs.com/Janous/p/7526024.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值