2018-2019 ACM-ICPC Southeastern European Regional (SEERC 2018) C Tree(level 2)(树的直径)(4种解法)

题目链接

题意:

给你一棵n个点的树(n<=100),每一个点有白/黑色,让你选m个黑色的点,

使得你选的这m个点的集合里最远的两个点的距离最小

解析:

这道题我训练的时候是用st的LCA求两点距离+二分+最大团验证来做的,代码有167行

比赛的时候...估计得写将近1个小时,然后还被自己LCA模板上的一个数组大小卡了半个小时...

这道题赛后看了大佬们的代码,大多都是和树的直径联系在一起的。

可以看一下树的直径及其证明。

里面有一个很重要的性质,就是树上一个点x最远能到达的点一定是直径的一个端点

这道题做法很多,首先一个比较简单版本的就是枚举任意两个点x,y,记录他们的距离为最长距离res

然后把剩余的点k加进来,如果dis[x][k]<=res&&dis[k][y]<=res,那么这个点就是可以加入的

如果最后的点数>=m,那么对答案进行更新

这里为什么点k满足dis[x][k]<=res&&dis[k][y]<=res就可以加入进来,保证k与集合里面的其他点的距离都<=res?

那么下面是证明

 

假定我们枚举的边是st,然后x,y都加入了集合

su=编号1,uv=编号5,vt=编号2,ux=编号4,vy=编号3

那么x,y加入集合条件是1+4<=1+5+2,  4+5+2<=1+5+2

=>4<5+2 && 4<=1

同理3<=5+1 &&  3<=2

那么我们证明4+3+5的长度

4+3+5(xy)<= 1+3+2(st)

那么就满足了条件了

所以这个思想得到的一个结论是

一条树链xy的长度为p,,如果两个点s,t都满足dis[s/t][x]<=p&&dis[s/t][y]<=p

那么dis[s][t]一定满足<=p

代码来源于Engineering Drawing

#include <bits/stdc++.h>
using namespace std;
const int N = 100 + 5;
vector<int> G[N];
int dis[N][N], level[N], col[N], n, m;
void addedge(int u, int v) {
    G[u].push_back(v);
    G[v].push_back(u);
}
void bfs(int s) {
    memset(level, -1, sizeof level);
    queue<int> q;
    level[s] = 0;
    q.push(s);
    while(!q.empty()) {
        int u = q.front(); q.pop();
        for(int v : G[u])
            if(level[v] == -1) {
                level[v] = level[u] + 1;
                q.push(v);
            }
    }
    for(int i = 1; i <= n; i++)
        dis[s][i] = level[i];
}
int main() {
    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> col[i];
    for(int i = 1; i <= n - 1; i++) {
        int u, v; cin >> u >> v;
        addedge(u, v)
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值