Problem - F - Codeforces

题目大意:有一棵n个点的树,其中有k个标记点,令点i到所有标记点的最远距离为fi,问所有点中fi的最小值是多少

1<=k<=n<=2e5

思路:我们首先考虑取得最小值的点在哪,我们假设这棵树是一条链,链上有两个标记点,如下图

F. Minimum Maximum Distance Codeforces Round 903 (Div. 3)-LMLPHP

显然,在标记点2、4之间的点是才可能是取得最小值的点,因为如果在某一个点一段的话,这个最大值一定大于两个标记点之间的距离,而在中间的点一定都是小于这个距离的,那么在这两个点之间很显然最中间的点是取得最小值的点。

所以取最小值的点一定在两个标记点的正中间,同时这两个标记点要距离最远,答案距离就是这个距离除以2向上取整。

要找两个距离最远的标记点,我们从任意一点i出发bfs找到一个最远的标记点j,这时有两种情况,一种就是这i,j就是距离最远的两个点,另一种情况是i在j和距离j更远的点k之间,

F. Minimum Maximum Distance Codeforces Round 903 (Div. 3)-LMLPHP

如上图,从4出发找到最远的点2,但右边的5距离2更远,这时候因为2已经是最靠左的一个点了,所以只要再从2再跑一遍bfs,找到的最远的点和2一定是距离最远的两个点,类似于问以哪个标记点为根,能找到根到标记点的最远距离。

//#include<__msvc_all_public_headers.hpp>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
vector<pair<int,int>>fac;
int tot = 0;
int n;
int head[N];
struct Edge
{
    int v, next;
}e[N*2];
int mark[N];
void addedge(int u, int v)
{
    e[++tot].v = v;
    e[tot].next = head[u];
    head[u] = tot;
}
bool vis[N];
void init()
{
    for (int i = 1; i <= n; i++)
    {
        vis[i] = 0;
        head[i] = -1;
        mark[i] = 0;
    }
}
void solve()
{
    int m;
    cin >> n;
    cin >> m;
    init();
    int it1 = -1;
    for (int i = 1; i <= m; i++)
    {
        int x;
        cin >> x;
        mark[x] = 1;
        if (it1 == -1)
            it1 = x;//任意找一个标记点作为起始根
    }
    for (int i = 1; i <= n - 1; i++)
    {
        int u, v;
        cin >> u >> v;
        addedge(u, v);
        addedge(v, u);
    }
    if (m == 1)
    {//如果只有1个标记点,那取最小值的点点就是那个标记点
        cout << 0 << '\n';
        return;
    }
    queue<pair<int, int>>q;
    q.push({ it1,0 });
    int ma = 0, mai;
    vis[it1] = 1;
    while (!q.empty())
    {//bfs找到距离根最远的点
        int u = q.front().first, nd = q.front().second;
        q.pop();
        for (int i = head[u]; ~i; i = e[i].next)
        {
            int v = e[i].v;
            if (vis[v])
                continue;
            vis[v] = 1;
            q.push({ v,nd + 1 });
            if (mark[v] && nd + 1 > ma)
            {
                ma = nd + 1, mai = v;
            }
        }
    }
    q.push({ mai,0 });//以之前找到的最远标记点为新根
    vis[mai] = 1;
    ma = 0, mai = -1;
    for (int i = 1; i <= n; i++)
    {
        vis[i] = 0;
    }
    while (!q.empty())
    {
        int u = q.front().first, nd = q.front().second;
        q.pop();
        for (int i = head[u]; ~i; i = e[i].next)
        {
            int v = e[i].v;
            if (vis[v])
                continue;
            vis[v] = 1;
            q.push({ v,nd + 1 });
            if (mark[v] && nd + 1 > ma)
            {
                ma = nd + 1, mai = v;
            }
        }
    }
    cout << (ma - 1) / 2 + 1;//答案就是最远距离/2
    cout << '\n';
}
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    //FILE* stream1;
    //freopen_s(&stream1, "in.txt", "r", stdin);
    //freopen_s(&stream1, "out.txt", "w", stdout);
    int t;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}
10-17 23:51