• cf592d


    题意:给出一个无根树,点数为10^5,所有边的长度为1。给定其中有一些点是受到攻击的。

    现在要求一个人选定一个点作为起点,走遍所有的受攻击点(不用再回到起点)。

    需要的最短距离是多少,选定的起点是哪个。

    分析:这是一个求树直径的变形题,原版请看这里

    求树的直径即树中最远的点对,需要进行两次bfs或者dfs。这里我们用的是dfs。

    首先我们假设所有点都受到攻击,那么要走完所有点再回到起点,路程L就是树上所有边的长度和乘以2。

    不用回到起点遍历所有点的最短距离就是用这个L减去树上的最远两点之间的距离。

    我们要把起点选在最远点对中的点才能保证所得解最小。

    但本题中不是所有点都受到攻击,所以在求最远点对的时候要进行一些改变。要找最远的受攻击点对。

    首先要以一个受攻击点为根(这个是本题最大难点,不太容易想到),进行第一次dfs,找到最深的受攻击点之后,再以它为根找最深的受攻击点。(注意长度相同时,找编号最小的)

    再求出以任意受攻击点为根,遍历所有受攻击点的路程和。两者相减即为解。

    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int MAX_N = (int)(2e5) + 20;
    
    int n, m;
    int root;
    vector<int> g[MAX_N];
    bool attacked[MAX_N];
    int depth[MAX_N];
    long long path_len;
    
    void input()
    {
        scanf("%d%d", &n, &m);
        for (int i = 0; i < n - 1; i++)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            a--;
            b--;
            g[a].push_back(b);
            g[b].push_back(a);
        }
        memset(attacked, 0, sizeof(attacked));
        for (int i = 0; i < m; i++)
        {
            int a;
            scanf("%d", &a);
            a--;
            attacked[a] = true;
        }
    }
    
    void dfs(int u, int father)
    {
        for (int i = 0; i < (int)g[u].size(); i++)
        {
            int v = g[u][i];
            if (v == father)
                continue;
            depth[v] = depth[u] + 1;
            dfs(v, u);
        }
    }
    
    int find_next()
    {
        int ret = root;
        for (int i = 0; i < n; i++)
        {
            if (!attacked[i])
                continue;
            if (depth[i] > depth[ret] || (depth[i] == depth[ret] && i < ret))
            {
                ret = i;
            }
        }
        return ret;
    }
    
    bool cal_depth(int u, int father)
    {
        bool ret = false;
        for (int i = 0; i < (int)g[u].size(); i++)
        {
            int v = g[u][i];
            if (v == father)
                continue;
            if (!cal_depth(v, u))
                continue;
            path_len += 2;
            ret = true;
        }
        return ret || attacked[u];
    }
    
    int main()
    {
        input();
        root = find(attacked, attacked + n, true) - attacked;
    
        depth[root] = 0;
        dfs(root, -1);
        root = find_next();
    
        depth[root] = 0;
        dfs(root, -1);
    
        int end = find_next();
        int city = min(root, end);
        path_len = 0;
        cal_depth(root, -1);
        printf("%d
    %I64d
    ", city + 1, path_len - depth[end]);
        return 0;
    }
    View Code
  • 相关阅读:
    [总结]2020年8月 OI学习/刷题记录
    [总结]2020年7月 OI学习/刷题记录
    [总结]2020年6月 OI学习/刷题记录
    [总结]2020年5月 OI学习/刷题记录
    [Luogu P6474][NOI Online #2 入门组]荆轲刺秦王 题解(BFS)
    [总结]2020年4月 OI学习/刷题记录
    [总结]2020年3月 OI学习/刷题记录
    [Luogu P6059]纯粹容器 senpai
    [Luogu P6044]「ACOI2020」惊吓路径
    ubuntu下查看(改变)本地端口开放情况,开启和关闭防火墙
  • 原文地址:https://www.cnblogs.com/rainydays/p/4934719.html
Copyright © 2020-2023  润新知