top1编程
← 返回题目
题解

纸牌PK

1 条题解

  • 0
    @ 2026-8-6 0:57:59

    P4758 纸牌PK(基础)

    解题思路

    小童和小程各抽 m 轮纸牌,每轮抽连续的一段(L 到 R 张),谁 m 轮抽到的数字总和更大,谁就赢。我们要想办法又快又准地算出两个人各自的总分。

    第一步,先想想直接一个一个加为什么太慢。 如果每轮都从 L 到 R 一张张加,一轮最多有 500000 张牌,m 轮最多 100 轮,最坏情况要加 5 千万次,太慢了,很可能会超时。

    第二步,认识神器"前缀和"。 聪明的方法叫"前缀和"(prefix sum)。我们开一个数组 prefixSum,prefixSum[i] 表示前 i 张牌的数字之和,也就是第 1 张加到第 i 张。这个数组可以在读牌的时候顺便建好: prefixSum[i] = prefixSum[i-1] + 第 i 张牌

    可以把它想象成一把"刻度尺":每一格都记下从头量到这里的总长度。

    第三步,用前缀和快速求任意一段区间和。 有了 prefixSum,想求任意一段 [L, R] 的和,只需要一步减法: 区间 [L, R] 的和 = prefixSum[R] - prefixSum[L-1]

    为什么?prefixSum[R] 是第 1 到 R 张的和,prefixSum[L-1] 是第 1 到 L-1 张的和,两者一减,中间的 L 到 R 张正好被单独算出来。就像用刻度尺先量出到 R 的总长,再减去到 L-1 的长,中间那段就是答案。

    第四步,把两个人的分数分别加起来再比较。 先读小童的 m 轮范围,每轮的区间和都加到 totalTong 上;再读小程的 m 轮范围,加到 totalCheng 上。最后比较:

    • totalTong 大,输出 T;
    • totalCheng 大,输出 C;
    • 两个一样大,输出 D。

    第五步,注意数据范围,小心溢出。 所有牌最多 500000 张,每张最大 100,一轮区间和最大 5 千万,m 轮加起来可能超过 20 亿,已经超出 int 的范围,所以 totalTong、totalCheng 和 prefixSum 数组都要用 long long。

    参考代码

    // 纸牌PK:前缀和快速求区间和,比较小童和小程各自m轮的总分大小
    #include <cstdio>
    using namespace std;
    
    int cardValue[500005];          // 每张纸牌上的数字
    long long prefixSum[500005];    // 前缀和数组,prefixSum[i]=前i张牌数字之和
    
    int main() {
        int cardCount, roundCount;
        scanf("%d%d", &cardCount, &roundCount);
        for (int index = 1; index <= cardCount; index++) {
            scanf("%d", &cardValue[index]);
            prefixSum[index] = prefixSum[index - 1] + cardValue[index];   // 一边读一边建前缀和
        }
        long long totalTong = 0, totalCheng = 0;   // 小童、小程各自的总和
        int left, right;
        for (int roundIndex = 0; roundIndex < roundCount; roundIndex++) {   // 先读小童的roundCount轮抽牌范围
            scanf("%d%d", &left, &right);
            totalTong += prefixSum[right] - prefixSum[left - 1];   // 区间[left,right]的和 = prefixSum[right]-prefixSum[left-1]
        }
        for (int roundIndex = 0; roundIndex < roundCount; roundIndex++) {   // 再读小程的roundCount轮抽牌范围
            scanf("%d%d", &left, &right);
            totalCheng += prefixSum[right] - prefixSum[left - 1];
        }
        if (totalTong > totalCheng) printf("T\n");
        else if (totalCheng > totalTong) printf("C\n");
        else printf("D\n");
        return 0;
    }
    

    复杂度分析

    建前缀和需要 O(n) 的时间,之后每轮区间和只要 O(1) 一次减法,总共 m 轮、两个人共 2m 次查询,所以总时间复杂度是 O(n + m)。n 最大 500000,m 最大 100,几毫秒就能跑完。空间上需要 prefixSum 数组,长度 n+1,空间 O(n)。因为用了 scanf 快速读入,500000 个数也能轻松读进来。

    • 1