题解 ARC116E【Spread of Information】

· · 题解

题目大意

一个城市是由 n 个点 n-1 条边组成的树,一个点的危险度是指这个点到任意一个消防站的距离的最小值。现在要选择 k 个点建立消防站,使得所有点的危险度的最大值最小,求这个值。(1\leq k<n\leq 2\times 10^5

题解

实现方式:

  1. 如果 p\ge q,那么说明不能被覆盖的点可以被其它点覆盖,自然也能覆盖 u,所以我们更新 b_u=-1,a_u=p

  2. 如果 q<mid,那么我们不覆盖节点 u,在该节点设立消防站不会使答案更优。更新 a_u=0,b_u=q

  3. 否则,我们需要在 u 上设立消防站,更新 a_u=mid,b_u=-1,计数 num+1

代码

#include <bits/stdc++.h>

using namespace std;

int n, k, ans, a[200005], b[200005], num, len;
vector<int> T[200005];

void dfs(int u, int fa)
{
    int p = -1, q = 0;
    for (int i = 0 ; i < T[u].size() ; i ++)
    {
        int v = T[u][i];
        if (v != fa)
        {
            dfs(v, u);
            p = max(p, a[v] - 1);
            q = max(q, b[v] + 1);
        }
    }
    if (p >= q)
    {
        a[u] = p;
        b[u] = -1;
        return ;
    }
    if (q < len)
    {
        a[u] = 0;
        b[u] = q;
        return ;
    }
    num ++;
    a[u] = len;
    b[u] = -1;
}

bool check(int x)
{
    memset(a, -1, sizeof(a));
    memset(b, -1, sizeof(b));
    len = x, num = 0;
    dfs(1, 0);
    if (b[1] != -1)
    {
        num ++;
    }
    if(num > k)
    {
        return 0;
    }
    return 1;
}

int main()
{
    cin >> n >> k;
    for (int i = 1 ; i < n ; i ++)
    {
        int u, v;
        cin >> u >> v;
        T[u].push_back(v);
        T[v].push_back(u);  
    }
    int l = 1, r = 200000;
    while (l <= r)
    {
        int mid = (l + r) >> 1;
        if (check(mid))
        {
            r = mid - 1;
            ans = mid;
        }
        else
        {
            l = mid + 1;
        }
    }
    cout << ans;
}