1 条题解

  • 0
    @ 2026-4-2 22:37:23

    题目理解

    我们有一个长度为 nn 的数组 aa,初始分数为 00。每次操作可以选择两个之前都没被选过的下标 iijji<ji < j),且满足 ai=aja_i = a_j,然后分数加 11。问最多能获得多少分。


    关键观察

    11. 元素独立配对
    由于每次操作选择两个下标,且要求它们对应的数值相等,不同数值之间的操作不会互相影响。因此,我们可以分别考虑每个数值 xx

    22. 频率计数
    fxf_x 表示数值 xx 在数组 aa 中出现的次数。
    每次选择两个相等的数值 xx,就会消耗掉两个值为 xx 的元素。
    因此,对于数值 xx,最多能进行的操作次数是 fx2\left\lfloor \frac{f_x}{2} \right\rfloor(即两两配对后剩余不足一对的无法使用)。

    33. 总分数
    总分数就是所有不同数值 xx 能配对的次数之和:

    $$\text{ans} = \sum_{x} \left\lfloor \frac{f_x}{2} \right\rfloor $$

    其中 xx 的取值范围是数组中出现过的所有数值。


    算法步骤

    11. 读入测试数据组数 tt22. 对每组测试数据:

    • 读入 nn 和数组 aa
    • 因为题目保证 1ain1 \le a_i \le n,所以可以开一个长度为 n+1n+1 的计数数组 cntcnt,用于统计每个数值的出现次数。
    • 遍历数组 aa,对于每个 aia_i,执行 cnt[a[i]]++cnt[a[i]]++
    • 遍历 xx11nn,将 cnt[x]/2cnt[x] / 2 累加到答案中。
    • 输出答案。

    时间复杂度

    • 每组数据遍历数组一次统计频率:O(n)O(n)
    • 遍历数值范围计算答案:O(n)O(n)
    • 总复杂度 O(tn)O(t \cdot n),在 t500t \le 500n20n \le 20 的条件下完全可行。

    示例验证

    以输入:

    4
    1 2 3 1
    

    为例:

    • 数值 11 出现 22 次 → 2/2=1\lfloor 2/2 \rfloor = 1
    • 数值 22 出现 11 次 → 1/2=0\lfloor 1/2 \rfloor = 0
    • 数值 33 出现 11 次 → 1/2=0\lfloor 1/2 \rfloor = 0
    • 答案 = 1+0+0=11 + 0 + 0 = 1,与题目输出一致。

    代码实现

    #include <iostream>
    #include <vector>
    using namespace std;
    
    int main() {
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            vector<int> a(n);
            vector<int> cnt(n + 1, 0); // 统计每个数值出现的次数,范围 1..n
            
            for (int i = 0; i < n; i++) {
                cin >> a[i];
                cnt[a[i]]++;
            }
            
            int ans = 0;
            for (int x = 1; x <= n; x++) {
                ans += cnt[x] / 2; // 每对相同的数贡献 1 分
            }
            
            cout << ans << endl;
        }
        return 0;
    }
    
    • 1

    信息

    ID
    6286
    时间
    1000ms
    内存
    256MiB
    难度
    3
    标签
    递交数
    4
    已通过
    1
    上传者