其他分享
首页 > 其他分享> > CF #781 Div. 2 C. Tree Infection

CF #781 Div. 2 C. Tree Infection

作者:互联网

题面翻译:

给出一个有 n 个顶点的有根树,节点 1 为根节点。一开始,所有节点都是 健康的。
每秒将进行 两次操作,先开始传染 spreading,然后进行注射 injection
  1. spreading: 对于每一个节点 v,如果它有一个已感染的子女,你可以选择对 v 的其他子女进行感染
  2. injecting:选择任意一个健康节点进行感染

这个过程每秒都重复执行直到整棵树被感染。你需要找出感染整棵树需要的最小秒数。

原题链接 [codeforces.com]

思路:

贪心、堆

首先可以发现,一个节点的子女 和 另一个节点的子女 是独立的。那我们可以按照父节点进行分组,就是所有兄弟节点在同一个组内,我们记组数为 每个组的节点数量为ci

因为是独立的,那么说明至少需要进行 m 次注射操作。每秒只能注射一次,所以同时 m 秒也是下限。

然后我们考虑怎么节省时间。第一秒过后,我们注射一个节点之前,可以直接感染前面注射点的健康兄弟节点。然后等到所有分组都被有节点被注射后(第 m 秒结束)。我们就看看有没有哪个组还有很多健康节点,有就去进行注射操作节省时间。

那说明我们要尽量减少 m 秒之后花费的时间,即,尽量在第 m 秒结束之前感染尽可能多的节点。

可以发现,在第 j 秒注射的组,在到第 m 秒结束时就能总共感染 min(m - j + 1, ci的节点。可以看出如果一个组已经全感染了,那他就对节省时间没有贡献了。那么进行贪心,把组按照节点个数从大到小排序,尽量让节点数多的减掉更多,贡献就是最大的。

考虑 m 秒以后。此时存活的组每秒都会减少一个健康节点,注射可以给特定组减多一个健康节点。直到健康节点最多的那个组也少于 3 个,那么下一秒它也全部感染了。题目整颗树节点最多2×105,过程是 O(n * log2n) 的,可以直接模拟。

实现:

我们用数组去记录每个节点的子女数量,就完成了分组。m 秒之后的部分,全部减一不会影响到堆的结构,直接在外面记录减了多少就行。注射操作用一个数组记录,下标 i 的位置就表示第 i 组注射了多少,进堆的时候排序关键字就是 (原数量(m 秒之后的) - 注射次数)。

代码实现
#include <bits/stdc++.h>

using namespace std;
using ll = long long;
const int MAX_N = 2e5;

int chd[MAX_N + 1];
vector<int> grp;
int t[MAX_N + 1];

struct cmp
{
    bool operator()(int a, int b) const
    {
        return grp[a] + t[a] < grp[b] + t[b];
    }
};

priority_queue<int, vector<int>, cmp> q;

void solve()
{
    memset(chd, 0, sizeof(chd));
    memset(t, 0, sizeof(t));
    while (q.size()) q.pop();
    grp.clear();
    int n;
    scanf("%d", &n);

    chd[0] = 1;
    for (int i = 2; i <= n; ++i)
    {
        int pi;
        scanf("%d", &pi);
        ++chd[pi];
    }

    
    for (int i = 0; i <= n; ++i)
    {
        if (chd[i]) grp.push_back(chd[i]);
    }

    sort(grp.begin(), grp.end(), greater<int>());
    n = grp.size();

    for (int i = 0; i != n; ++i)
    {
        // after all groups injected;
        grp[i] -= n - i;
        if (grp[i] > 0) q.emplace(i);
    }

    int exa = 0;
    while (q.size())
    {
        int i = q.top();
        q.pop();
        ++exa;
        int will = grp[i] + t[i] - exa;
        if (will <= 0)
        {
            if (will < 0) --exa;
            break;
        }
        --t[i]; //inj
        if (will >= 1) q.push(i);
    }

    printf("%d\n", exa + n);
}

int main(void)
{
    int cases;
    scanf("%d", &cases);
    while (cases--) solve();
    return 0;
}

 

标签:chd,grp,int,感染,Tree,CF,Div,节点,注射
来源: https://www.cnblogs.com/julic20s/p/cf-781-2c.html