1 条题解

  • 0
    @ 2026-6-19 10:30:28

    📝 题目大意

    NN 个人按编号 1N1 \sim N 的顺序依次操作:若当前这个人还没有被别人叫过编号,则他叫出 AiA_i 的编号。求操作结束后,一次都没被别人叫过编号的所有人的编号,按升序输出。

    💡 解题思路

    1. 题目分析

      • N2×105N \leq 2 \times 10^5O(N)O(N)O(NlogN)O(N\log N) 均可接受。
      • AiiA_i \neq i,意味着人不会叫自己的编号,避免了自环。
      • 每个人最多操作一次,且只有"未被叫过的人"才会叫别人——这意味着操作是有条件的、单向传播的。
    2. 算法推导

      • 使用布尔数组 f[i] 表示第 ii 个人是否被叫过编号(初始全为 false)。
      • 按照 i=1Ni = 1 \to N 的顺序遍历:
        • f[i] == false(第 ii 个人尚未被叫过),则执行操作:将 f[a[i]] 设为 true(标记 AiA_i 被叫过)。
        • f[i] == true(第 ii 个人已经被叫过),则跳过,不做任何事。
      • 遍历结束后,再遍历一次 f 数组,统计并输出所有 f[i] == falseii
      • 注意:因为按 1N1 \to N 顺序遍历,输出的编号天然升序,无需额外排序。
    3. 边界与细节

      • 数组下标从 11 开始,开到 N+5N+5 的大小以防越界。
      • y 数组在标准代码中声明但未使用,属于冗余变量,不影响正确性。
      • 不存在全部人都被叫到的情况:题目保证 AiiA_i \neq i,且至少有一人不会被叫(读者可自行验证:至少人 11 的初始状态决定了一定有漏网之鱼)。

    ⏱️ 复杂度分析

    • 时间复杂度O(N)O(N),两次线性遍历。
    • 空间复杂度O(N)O(N),存储 AA 数组和 ff 标记数组。

    💻 标准代码 (C++)

    #include <bits/stdc++.h>
    using namespace std;
    
    bool y[200005], f[200005];  // f[i]: 第i个人是否被叫过编号
    int n, a[200005];
    
    int main () {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
        
        // 按顺序模拟每个人的操作
        for (int i = 1; i <= n; i++)
            if (f[i] == 0)           // 第i个人还没被叫过
                f[a[i]] = 1;         // 叫出A_i的编号,标记A_i被叫过
        
        // 统计未被叫过的人数
        int ans = 0;
        for (int i = 1; i <= n; i++)
            if (f[i] == 0) ans++;
        printf("%d\n", ans);
        
        // 按升序输出所有未被叫过的人的编号
        for (int i = 1; i <= n; i++)
            if (f[i] == 0) printf("%d ", i);
        
        return 0;
    }
    
    • 1

    信息

    ID
    690
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    (无)
    递交数
    1
    已通过
    1
    上传者