其他分享
首页 > 其他分享> > CF796C 攻击银行

CF796C 攻击银行

作者:互联网

1 CF796C 攻击银行

2 题目描述

时间限制 \(2s\) | 空间限制 \(256M\)

给出一个有 \(n\) 个银行和 \(n-1\) 根连接它们的电线的地图,银行从 \(1\) 到 \(n\) 依次编号,地图上的每个银行有一个防火墙强度 \(a_i\) 和两种可能的状态:在线或者下线。如果两个银行之间有一根电线直接连接,我们称它们之间的关系为相邻;如果银行 \(i\) 与银行 \(k\) 之间有一根电线,银行 \(j\) 与银行 \(k\) 之间也有一根电线,且银行 \(k\) 状态为在线,我们称银行 \(i\) 和银行 \(j\) 之间的关系为半相邻。

我们决定攻击所有银行,为了击败所有银行,我们需要确定我们的攻击强度 \(k\)。

如果我们要攻击一个银行,该银行必须满足以下三个条件:

\(1.\) 该银行在被攻击前状态为在线。

\(2.\) 该银行至少与一个状态为下线的银行相邻。

\(3.\) \(a_i \le k\)。

当一个银行被攻击了,它的状态就变为了下线,且所有与其相邻与半相邻的银行的防火墙强度都\(+1\)。

请求出可以成功击败所有银行的最小攻击强度 \(k\)。

3 输入格式

第一行包括一个整数 \(n\)(\(1 \le n \le 3 * 10^5\))。

第二行包括 \(n\) 个整数 \(a_1,a_2,a_3,...,a_n\)(\(-10^9 \le a_i \le 10^9\))。

接下来的 \(n-1\) 行,每行包括两个整数 \(u_i, v_i\)(\(1 \le u_i, v_i \le n\),\(u_i \ne v_i\)),代表银行 \(u_i\) ,\(v_i\) 之间有电线连接。

电线连接的方式保证一定存在一个攻击强度使得我们可以击败所有银行。

4 输出格式

一行包含一个整数,表示最小的攻击强度。

5 样例输入输出

样例1输入 样例1输出 样例2输入 样例2输出 样例3输入 样例3输出
5
1 2 3 4 5
1 2
2 3
3 4
4 5
5 7
38 -29 87 93 39 28 -55
1 2
2 5
3 2
2 4
1 7
7 6
93 5
1 2 7 6 7
1 5
5 3
3 4
2 4
8

6 样例解释

样例 \(1\),我们可以用 \(5\) 的攻击强度攻占所有银行:

样例 \(2\),我们可以依次攻击银行 \(4\),\(2\),\(3\),\(1\),\(5\),\(7\) 和 \(6\),这样我们就可以用 \(93\) 的攻击强度攻占所有银行。

7 题解

我们发现:我们最先选择的节点可以看作根节点,而整棵树就可以看作一个从根节点指向叶子节点的有向图。因此,与根节点相邻的节点的防火墙强度均会 \(+1\)。而剩余的所有节点的防火墙强度在被遍历到之前都会被与其半相邻和相邻的节点分别增加一次防火墙强度,最终的防火墙强度会 \(+2\)。由于我们的攻击强度 \(k\) 保持不变,所以 \(k\) 的最小值就是该树的中所有节点的防火墙强度在增加后的最大值。

综上,题目可以被简化成:给出一棵树,请选出最恰当的根节点,使得在所有节点的权值依据上述规则增强后的最大值最小。我们不难发现:最终的答案与所有节点中权值的最大值与次大值有关。以下是所有可能的方案:

\(1.\) 当整棵树中只有一个最大值 \(maxn\) 时,容易发现最优情况一定是将该节点作为根节点计算。这时,我们只需要计算出整个图中权值等于 \(maxn - 1\) 的节点的个数 \(cnt_2\) 和与根节点相邻的权值等于 \(maxn - 1\) 的节点的个数 \(cnt_3\),并比较它们的大小即可。

如果 \(cnt_2 = cnt_3\),那么答案肯定是 \(maxn\)。这是因为根节点的权值恒为 \(maxn\),而所有与根节点相邻的节点中最大权值 \(maxn - 1\) 在 \(+1\) 后变为了 \(maxn\),与根节点权值相同。由于 \(cnt_2 = cnt_3\),所有的权值为 \(maxn - 1\) 的节点的最终权值都是 \(maxn\)。答案也就是 \(maxn\)。

当 \(cnt_2 > cnt_3\) 时,不与根节点相邻的节点中存在权值为 \(maxn - 1\) 的节点。由于不与根节点相邻的节点权值全部增加 \(2\),这些节点最终的权值会变成 \(maxn + 1\),大于根节点和与根节点相邻的权值为 \(maxn - 1\) 的节点的最终权值 \(maxn\),故答案应为 \(maxn + 1\)。

\(2.\) 当整棵树中有多个最大值 \(maxn\) 时,我们只需计算所有节点与权值为 \(maxn\) 的节点相邻的个数(如果该节点本身就是权值为 \(maxn\) 的节点,将该个数 \(+1\)),然后找出其中个数的最大值即可。

如果个数的最大值等于权值为 \(maxn\) 的节点的个数 \(cnt\),那么我们得到的答案肯定是 \(maxn + 1\),只需要以这个相邻 \(maxn\) 个数最多的节点为根节点建立整个图,最大的权值一定为 \(maxn+1\)。因为 \(cnt>1\),所以即便我们以权值为 \(maxn\) 的节点为根节点,仍然会有至少一个权值为 \(maxn\) 的节点与根节点相邻,而该节点的权值最终变为 \(maxn + 1\)。为什么我们不需要考虑所有权值等于 \(maxn - 1\) 的节点呢?这是因为这些节点的权值最多增加 \(2\),而增加 \(2\) 以后,最大的可能的权值也是 \(maxn + 1\),与答案相同,不影响答案。

如果个数的最大值小于权值为 \(maxn\) 的节点的个数 \(cnt\),那么无论如何建图肯定会有至少一个权值为 \(maxn\) 的节点不与根节点相邻,因此答案一定为 \(maxn + 2\)。

8 代码(空格警告):

#include <iostream>
using namespace std;
const int N = 6e5+10;
int n, u, v, tot, maxn, cnt, pos, ans, cnt2, cnt3;
int a[N];
int head[N], ver[N], last[N], c[N];
bool f[N];
void add(int x, int y)
{
    ver[++tot] = y;
    last[tot] = head[x];
    head[x] = tot;
}
int main()
{
    maxn = -2147483647;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        if (a[i] > maxn)
        {
            maxn = a[i];
            pos = i;
        }
    }
    for (int i = 1; i <= n; i++)
    {
        if (maxn == a[i])
        {
            cnt++;
            c[i]++;
        }
    }
    for (int i = 1; i < n; i++)
    {
        cin >> u >> v;
        add(u, v);
        add(v, u);
        if (a[u] == maxn) c[v]++;
        if (a[v] == maxn) c[u]++;
    }
    if (cnt == 1)
    {
        for (int i = 1; i <= n; i++) if (a[i] == maxn - 1) cnt2++;
        for (int i = head[pos]; i; i = last[i])
        {
            int y = ver[i];
            if (a[y] == maxn - 1) cnt3++;
        }
        if (cnt2 == cnt3) ans = maxn;
        else ans = maxn + 1;
    }
    else
    {
        for (int i = 1; i <= n; i++) cnt2 = max(cnt2, c[i]);
        if (cnt2 == cnt) ans = maxn + 1;
        else ans = maxn + 2;
    }
    cout << ans;
    return 0;
}

欢迎关注我的公众号:智子笔记

标签:cnt,攻击,银行,相邻,maxn,权值,CF796C,节点
来源: https://www.cnblogs.com/david24/p/14378479.html