top1编程
← 返回题目
题解

总之就是非常奇怪

1 条题解

  • 0
    @ 2026-8-6 0:09:19

    P4695 总之就是非常奇怪(【基础】)

    解题思路

    先看数组 k,里面出现过的数,乌鸡姐姐听不见,直接忽略。剩下没在 k 里的数字,每个数字第一次出现的时候她也听不见(耳背),之后再次出现才能听到。下面分四步实现。

    **第一步,快速读入。**数据量可能很大(可达千万级),所以用 fread 缓冲实现快速读入 readInt,用 putchar 缓存快速输出 writeInt,防止超时。

    **第二步,标记 k 里的数。**数字的值域只有 -9 到 9,加 9 就能当数组下标,所以用 isInK 数组标记:isInK[value + 9] = 1 表示 value 在 k 里出现过。

    **第三步,遍历听牌。**对 m 个数逐个处理:如果在 isInK 里就直接忽略;如果没在 k 里但这是第一次出现(seenOnce 数组对应位置还是 0),把 seenOnce 标记为 1 然后忽略;否则说明这次能听到,把数存进 heardNums。

    **第四步,输出。**每个测试样例输出一行,数之间用空格隔开;如果某个样例一个数都没听到(heardCnt 为 0),就什么都不输出,连换行也不输出。最后别忘了把输出缓冲区里的内容用 fwrite 清空。

    边界情况:数字范围只有 -9 到 9,加 9 当下标,数组开 25 个足够;注意第一次出现的数要"听过一次"但不算听到,第二次才计入。

    参考代码

    // P4695 总之就是非常奇怪:忽略数组k中出现过的数,剩下的数里第一次出现的也忽略,输出听到的数组
    // 数据量可达千万级,用 fread 缓冲实现快速读入,用 putchar 缓存快速输出
    #include <cstdio>
    using namespace std;
    
    const int BS = 1 << 20;
    char ibuf[BS];
    int ipos = 0, ilen = 0;
    
    // 从缓冲区读一个字符
    inline char gc() {
        if (ipos >= ilen) {
            ilen = fread(ibuf, 1, BS, stdin);
            ipos = 0;
            if (ilen == 0) return -1;
        }
        return ibuf[ipos++];
    }
    
    // 快速读入一个整数(支持负数)
    inline int readInt() {
        int num = 0, sign = 1;
        char c = gc();
        while (c < '0' || c > '9') {
            if (c == '-') sign = -1;
            c = gc();
        }
        while (c >= '0' && c <= '9') {
            num = num * 10 + c - '0';
            c = gc();
        }
        return num * sign;
    }
    
    const int OS = 1 << 20;
    char obuf[OS];
    int opos = 0;
    
    // 把字符写入输出缓冲区
    inline void pc(char c) {
        if (opos >= OS) {
            fwrite(obuf, 1, OS, stdout);
            opos = 0;
        }
        obuf[opos++] = c;
    }
    
    // 输出一个整数(不带空格)
    inline void writeInt(int value) {
        if (value < 0) { pc('-'); value = -value; }
        if (value == 0) { pc('0'); return; }
        char digits[12];
        int digitLen = 0;
        while (value) { digits[digitLen++] = '0' + value % 10; value /= 10; }
        while (digitLen) pc(digits[--digitLen]);
    }
    
    int heardNums[1000005];   // 存最终听到的数(全局数组)
    
    int main() {
        int T = readInt();
        while (T--) {
            int n = readInt(), m = readInt();
            int isInK[25] = {0};        // 值域 -9~9,加9做下标,标记在k中出现过的数
            for (int i = 0; i < n; i++) {
                int value = readInt();
                isInK[value + 9] = 1;
            }
            int heardCnt = 0;
            int seenOnce[25] = {0};     // 该数第一次出现是否已被忽略
            for (int i = 0; i < m; i++) {
                int value = readInt();
                if (isInK[value + 9]) continue;          // 在k中出现过,直接忽略
                if (!seenOnce[value + 9]) {              // 第一次出现,耳背听不见
                    seenOnce[value + 9] = 1;
                    continue;
                }
                heardNums[heardCnt++] = value;           // 之后的出现能听到
            }
            if (heardCnt) {
                for (int i = 0; i < heardCnt; i++) {
                    if (i) pc(' ');
                    writeInt(heardNums[i]);
                }
                pc('\n');
            }
            // heardCnt==0 时不输出任何内容(连换行也没有)
        }
        if (opos) fwrite(obuf, 1, opos, stdout);
        return 0;
    }
    

    复杂度分析

    每个数字只被处理一次,单组数据的时间复杂度是 O(n+m),其中 m 最大 100 万。空间上用一个长度 m 的数组存输出,是 O(m)。

    • 1