巨树(二分+贪心)

题目

题目描述

Elaine 把她的线段树插在 SunIsMe 的内存池中。她掏出背包内一卷古老的笔记, 吟唱 了半页代码, 线段树以肉眼可见的速度长高, 长大, 长出更多的枝条, 成了一颗通天的生 成巨树! 但是生成树过于庞大, 内存池内的数据无法及时传达到生成树的一些关键节点上。还好 XJC 带了 K 个蓝牙耳机, XJC 用反演魔法把这些耳机改造成了无线数据传输装置, 每一个传 输装置都能直接从内存池中汲取数据, 并且把数据无线广播出去。但是无线传输不是很可靠, 并且传输距离越远可靠性越差, 所以探索者们必须最小化这些距离。 现在探索者们想知道利用XJC的K个蓝牙耳机能让树上所有关键节点与最近的无线数据 传输装置距离的最大值最小为多少。 给出一颗 N 个节点的树和 M 个关键节点以及无线数据传输装置的数量 K, 树边长度为 1。 你需要把这 K 个无线数据传输装置安装到树的一些节点上, 并让关键节点离最近无线 传输装置的距离最大值最小, 求出这个最小值。 此处, 树上节点 A 和 B 之间的距离指 AB 两点间路径长度和。

输入格式

第一行输入三个正整数 N, M 和 K。 接下来一行输入 M 个正整数, 表示关键点的编号。 接下来 N-1 行, 每行两个正整数 a, b, 表示节点 a 和节点 b 之间有一条边。

输出格式

输出一行一个正整数表示你的答案。

样例

样例输入

5 2 1
1 4
1 2
1 3
2 4
4 5

样例输出

1

 分析

看到这种求最大值最小的就要用的二分,且这道题确实具有单调性

但是考场上也只想到了二分而已....

扫描二维码关注公众号,回复: 8860453 查看本文章

如果有了二分,我们就有多了一个条件:关键点与覆盖点距离不超过len(len为二分的答案)

有了这个条件,我们就可以考虑关键点与覆盖点的距离了

设以i为根的子树中最远的未被覆盖关键点离i的距离为tsl, 离最近的覆盖点距离为jzl

然后我们用贪心的思想

如果tsl == len时,那么我们就不得不在当前根节点加覆盖点了

所以tsl是一定不可能大于len了

否则,我们可以考虑这个最远的关键点可以被这个子树的其他覆盖点覆盖

所以如果最远的关键点都可以被覆盖,那么这颗子树所有关键点都可以被覆盖

我们现在已经选出了离根最近的覆盖点,有什么用呢?

其实,现在未被覆盖的关键点不可能是覆盖点的子节点(或后代)

所以,如果jzl+tsl<=len那么就能被覆盖

如果子树内没有被覆盖的关键点,则tsl = -1

最后还要判断整颗子树的根是否被覆盖

我们把不得不要加的覆盖点算出来

与k比较,如果大于k则len不满足

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;
const int MAXN = 200003;
int n , m , k;
struct node{
    int jzl , tsl;
};
vector<int>G[MAXN];
bool ts[MAXN];
int cnt;
int maxx;
node dfs( int x , int fa ){
    node now;
    now.jzl = 0x3f3f3f3f , now.tsl = -1;
    for( int i = 0 ; i < G[x].size() ; i ++ ){
        int v = G[x][i];
        if( v == fa ) continue;
        node tx = dfs( v , x );
        now.jzl = min( now.jzl , tx.jzl ) , now.tsl = max( now.tsl , tx.tsl );
    }
    now.jzl ++;
    if( now.tsl != -1 ) now.tsl ++;
    if( ts[x] ) now.tsl = max( now.tsl , 0 );
    if( now.jzl + now.tsl <= maxx ) now.tsl = -1;
    if( now.tsl == maxx ) now.tsl = - 1 , now.jzl = 0 , cnt ++;
    return now;
}
bool check( int x ){
    maxx = x;
    cnt = 0;
    node m = dfs( 1 , 0 );
    if( m.tsl != -1 ) cnt ++;
    if( cnt > k )
        return 0;
    return 1;
}
int main()
{
    scanf( "%d%d%d" , &n , &m , &k );
    for( int i = 1 ; i <= m ; i ++ ){
        int x;
        scanf( "%d" , &x );
        ts[x] = 1;
    }
    for( int i = 1 ; i < n ; i ++ ){
        int x , y;
        scanf( "%d%d" , &x , &y );
        G[x].push_back( y );
        G[y].push_back( x );
    }
    int l = 0 , r = n , ans = n + 1 ;
    while( l <= r ){
        int mid = ( l + r ) / 2;
        if( check( mid) )
            r = mid - 1 , ans = mid;
        else
            l = mid + 1;
    }
    printf( "%d" , ans );
    return 0;
}
发布了68 篇原创文章 · 获赞 7 · 访问量 3856

猜你喜欢

转载自blog.csdn.net/weixin_43823476/article/details/95518242