题解:AtCoder AT_awc0101_c Chain of Infection

【题目来源】

AtCoder:C - Chain of Infection

【题目描述】

Takahashi is the administrator of a network consisting of \(N\) computers and \(1\) central server. The \(N\) computers are numbered from \(1\) to \(N\), and the central server is numbered as computer \(0\). That is, the entire network consists of a total of \(N + 1\) computers: computers \(0, 1, \ldots, N\).

Each computer \(i\) (\(1 \leq i \leq N\)) is directly connected to a parent computer \(P_i\) (\(0 \leq P_i \leq N\), \(P_i \neq i\)). These connections form a tree structure with \(N + 1\) vertices rooted at the central server (computer \(0\)). In this tree structure, a child computer of computer \(v\) refers to a computer \(j\) satisfying \(P_j = v\).

One day, a virus infiltrated the network. Each computer \(i\) (\(1 \leq i \leq N\)) has a vulnerability value \(D_i\) assigned to it.

Virus infection targets only computers \(1\) through \(N\). The central server (computer \(0\)) is not subject to infection and will never become infected. Infection spreads in a chain reaction according to the following rules:

  1. Initial infection: All computers with a positive vulnerability value (\(D_i > 0\)) become infected. Computers with a vulnerability value of \(0\) or less (\(D_i \leq 0\)) are not infected at this stage. However, they may become infected later through Rule 2 below.

  2. Propagation of infection (child → parent direction): For a computer \(v\) (\(1 \leq v \leq N\)) that is not yet infected, let \(a\) be the number of infected child computers of \(v\), and \(b\) be the number of non-infected child computers of \(v\). If \(a > b\) is satisfied, then \(v\) becomes newly infected. This determination is made regardless of the vulnerability value \(D_v\) of computer \(v\) itself. That is, even if \(D_v \leq 0\), \(v\) becomes infected if the condition on the infection status of its child computers is met. Note that for a computer with no child computers, \(a = b = 0\), which does not satisfy \(a > b\), so such a computer will not become infected by this rule. Infection only occurs in the direction where a parent becomes infected based on the infection status of its children. Even if a parent computer becomes infected, this does not cause its child computers to become newly infected.

  3. Repetition: The determination and infection in Rule 2 are repeated until no new computers become infected in a round. The determination in each round is performed simultaneously for all computers based on the infection status at the beginning of that round. That is, if multiple computers become newly infected in a given round, they all become infected simultaneously, and the infection status does not change partway through the same round. The determination in the next round is based on the updated infection status.

Determine the total number of computers that are ultimately infected. The central server (computer \(0\)) is not subject to infection and should not be included in the count.

高橋是一个由 \(N\) 台计算机和 \(1\) 台中央服务器组成的网络的管理员。\(N\) 台计算机编号为 \(1\)\(N\),中央服务器编号为计算机 \(0\)。也就是说,整个网络共有 \(N + 1\) 台计算机:计算机 \(0, 1, \ldots, N\)

每台计算机 \(i\)\(1 \leq i \leq N\))直接与一台父计算机 \(P_i\)\(0 \leq P_i \leq N\)\(P_i \neq i\))相连。这些连接形成以中央服务器(计算机 \(0\))为根的、具有 \(N + 1\) 个顶点的树形结构。在此树形结构中,计算机 \(v\)子计算机指的是满足 \(P_j = v\) 的计算机 \(j\)

某天,一种病毒侵入了网络。每台计算机 \(i\)\(1 \leq i \leq N\))被分配了一个脆弱性值 \(D_i\)

病毒感染的目标仅限于计算机 \(1\)\(N\)。中央服务器(计算机 \(0\))不会被感染,也永远不会被感染。感染按照以下规则以连锁反应的方式传播:

  1. 初始感染: 所有脆弱性值为正(\(D_i > 0\))的计算机被感染。脆弱性值为 \(0\) 或以下(\(D_i \leq 0\))的计算机在此阶段不被感染。但是,它们可能通过下面的规则 2 在之后被感染。

  2. 感染的传播(子计算机 → 父计算机方向): 对于尚未被感染的计算机 \(v\)\(1 \leq v \leq N\)),设 \(a\)\(v\) 的被感染的子计算机数量,\(b\)\(v\) 的未被感染的子计算机数量。如果满足 \(a > b\),则 \(v\) 新被感染。这一判定与计算机 \(v\) 自身的脆弱性值 \(D_v\) 无关。也就是说,即使 \(D_v \leq 0\),如果其子计算机的感染状态满足条件,\(v\) 也会被感染。注意,对于没有子计算机的计算机,\(a = b = 0\),不满足 \(a > b\),因此这样的计算机不会通过此规则被感染。感染仅在父计算机基于其子计算机的感染状态而被感染的方向上发生。即使父计算机被感染,也不会导致其子计算机新被感染。

  3. 重复: 规则 2 的判定和感染重复进行,直到某一轮中没有新计算机被感染为止。每一轮中的判定对所有计算机基于该轮开始时的感染状态同时进行。也就是说,如果某一轮中多台计算机新被感染,它们同时被感染,同一轮中的感染状态不会中途改变。下一轮的判定基于更新后的感染状态。

求最终被感染的计算机总数。中央服务器(计算机 \(0\))不被计入。

【输入】

\(N\)
\(P_1\) \(D_1\)
\(P_2\) \(D_2\)
\(\vdots\)
\(P_N\) \(D_N\)

  • The first line contains an integer \(N\) representing the number of computers.
  • Lines \(2\) through \(N + 1\) give the information for each computer.
  • Line \(1 + i\) contains the parent computer number \(P_i\) of computer \(i\) and the vulnerability value \(D_i\) of computer \(i\), separated by a space.

【输出】

Output the total number of computers that are ultimately infected, in a single line.

【输入样例】

6
0 -1
1 1
1 2
1 0
2 -5
3 0

【输出样例】

3

【核心思想】

  1. 问题分析:给定一棵以中央服务器 \(0\) 为根的树,共 \(N+1\) 个节点。每台计算机 \(i\)\(1 \leq i \leq N\))有脆弱性值 \(D_i\),初始时 \(D_i > 0\) 的节点被感染。感染规则为:每轮同时检查所有未感染节点 \(v\),若其被感染的子节点数 \(a\) 大于未感染子节点数 \(b\)(即 \(a > b\)),则 \(v\) 被感染。此过程重复直到无新感染。需要求最终被感染的计算机总数(不含节点 \(0\))。

  2. 算法选择

    • DFS 后序遍历(树形 DP):从叶子向上计算每个节点的感染状态,利用树的层次结构天然模拟"从子到父"的传播方向
    • 多轮传播优化:关键观察是——一个节点被感染后,只会影响其父节点的判定。因此可以通过 DFS 后序遍历拓扑排序(按深度从大到小) 来模拟多轮传播
  3. 关键步骤

    • 建图:根据父节点关系 \(P_i\) 建立无向边,以 \(0\) 为根
    • 初始感染:标记所有 \(D_i > 0\) 的节点为已感染
    • 多轮传播模拟
      • 每轮遍历所有未感染节点,统计其子节点的感染状态
      • 若满足 \(a > b\),则标记该节点为待感染
      • 所有待感染节点在本轮结束时同时变为已感染
      • 重复直到某轮无新感染
    • 统计答案:遍历节点 \(1\)\(N\),统计 infected[i] == true 的数量
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \cdot H)\),其中 \(H\) 为树高(最坏情况 \(O(N^2)\),链状树每轮感染一层)。优化后可用 拓扑排序按深度递减处理 达到 \(O(N)\)
    • 空间复杂度:\(O(N)\),存储树的邻接表、感染状态数组、子节点计数数组
  5. DFS-树与多轮传播的核心思想

    • 传播方向确定性:感染只能从子节点传向父节点(\(child \to parent\)),不会反向传播,因此具有明确的层次性
    • 后序遍历的局限性:单次 DFS 后序遍历只能处理一轮判定,对于需要多轮才能感染的节点(如某节点第一轮不满足 \(a > b\),但第二轮子节点被感染后满足),需要重复处理或按深度递减顺序处理
    • 优化策略:按节点深度从大到小排序处理,或反复进行后序遍历直到无变化,确保所有可能的传播都被覆盖
    • 判定条件本质\(a > b\) 等价于 \(a > \frac{child\_count}{2}\),即被感染子节点数超过半数时父节点被感染,体现了"多数表决"机制
    • 适用于树形结构上的状态传播、层次依赖判定类问题

【算法标签】

DFS-树

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int N = 200005, M = N * 2;    // N: 最大计算机数, M: 最大边数(无向图双向边)
int n, ans;                         // n: 计算机数量(不含中央服务器0), ans: 最终被感染的计算机数
int d[N];                           // d[i]: 计算机i的脆弱性值
int a[N], b[N];                     // a[u]: u的被感染子计算机数, b[u]: u的未被感染子计算机数
bool infected[N];                   // infected[i]: 计算机i是否被感染
int h[N], e[M], ne[M], idx;         // 链式前向星存图

// 链式前向星:添加无向边
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

// DFS后序遍历:从叶子向上计算每个节点的感染状态
// 注意:题目规则3要求多轮传播,但此代码只做了单轮DFS
// 实际上该代码有bug:对于需要多轮才能感染的节点,只计算了一轮
void dfs(int u, int fa)
{
    // 遍历u的所有子节点
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int v = e[i];
        if (v == fa) continue;      // 跳过父节点

        dfs(v, u);                  // 递归处理子树

        // 统计子节点的感染状态
        if (infected[v] == 1)       // v被感染
            a[u]++;                 // u的被感染子计算机数+1
        else                        // v未被感染
            b[u]++;                 // u的未被感染子计算机数+1
    }

    // 判断u是否满足感染条件:被感染子节点数 > 未被感染子节点数
    // 注意:这个判断只在DFS回溯时做一次,没有考虑多轮传播
    // 例如:某个节点第一轮不满足条件,但第二轮子节点被感染后可能满足
    if (a[u] > b[u])
        infected[u] = 1;            // u被感染
}

int main()
{
    cin >> n;                       // 读入计算机数量

    // 初始化链式前向星
    memset(h, -1, sizeof(h));

    // 读入每台计算机的父节点和脆弱性值
    for (int i = 1; i <= n; i++)
    {
        int p, D;
        cin >> p >> D;
        add(i, p), add(p, i);       // 建立无向边(树结构)
        d[i] = D;                   // 记录脆弱性值

        // 初始感染:脆弱性值为正的计算机被感染
        if (d[i] > 0)
            infected[i] = 1;
    }

    // 从中央服务器0开始DFS
    // 0不会被感染,但作为根节点遍历整棵树
    dfs(0, -1);

    // 统计最终被感染的计算机数量(不包括0)
    for (int i = 1; i <= n; i++)
    {
        if (infected[i] == true)
            ans++;
    }

    // 输出最终被感染的计算机总数
    cout << ans << endl;

    return 0;
}

【运行结果】

6
0 -1
1 1
1 2
1 0
2 -5
3 0
3
posted @ 2026-06-30 20:45  团爸讲算法  阅读(2)  评论(0)    收藏  举报