1 条题解

  • 0
    @ 2026-4-1 19:02:30

    题目简述

    给定一个由 nn互不相同的正整数组成的数组 a1,a2,,ana_1, a_2, \dots, a_n。你可以选择一个正整数 kk1k10181 \le k \le 10^{18}),然后将每个 aia_i 替换为 aimodka_i \bmod k。要求操作后的数组中恰好包含两个不同的数值。题目保证这样的 kk 一定存在,你只需输出任意一个。


    解题思路

    f(k)f(k) 表示使用模数 kk 操作后,数组 aa 中不同余数的个数。
    我们尝试从 k=2k=2 开始,如果此时 f(2)=2f(2)=2,则直接输出 22。否则 f(2)=1f(2)=1,即所有数模 22 的余数相同,也就是说所有 aia_i 要么全是偶数,要么全是奇数。

    考虑一般情况:假设 f(k)=1f(k)=1,即所有 aimodka_i \bmod k 都等于同一个值 xx。那么对于 2k2k,每个 aia_i 可以写为 ai=qik+xa_i = q_i \cdot k + x

    • 如果 qiq_i 是偶数,则 aimod(2k)=xa_i \bmod (2k) = x
    • 如果 qiq_i 是奇数,则 aimod(2k)=x+ka_i \bmod (2k) = x + k

    因此,从 kk 翻倍到 2k2k 时,余数最多分裂成两个值 xxx+kx+k。若所有 qiq_i 同奇偶,则 f(2k)=1f(2k)=1,否则 f(2k)=2f(2k)=2

    由于 aia_i 互不相同且 n2n \ge 2f(k)f(k)k=1k=1 时的 11 开始,随着 kk 不断翻倍,最终一定会出现分裂(因为当 k>maxaik > \max a_i 时,aimodk=aia_i \bmod k = a_i,此时 f(k)=n2f(k)=n \ge 2)。因此,存在某个 mm 使得 f(2m)=1f(2^m)=1f(2m+1)=2f(2^{m+1})=2。我们只需从 k=2k=2 开始不断翻倍,直到 f(k)=2f(k)=2 为止。


    算法步骤

    1. 读入 tt 组数据。
    2. 对每组数据:
      • 读入 nn 和数组 aa
      • 初始化 k=2k = 2
      • 循环:
        • 计算所有 aimodka_i \bmod k 并放入集合 SS
        • 如果 S=2|S| = 2,输出 kk 并退出循环。
        • 否则 k2kk \leftarrow 2k

    复杂度分析

    • 每次检查 S|S| 需要 O(n)O(n) 时间。
    • kk 最多翻倍 O(logmaxai)O(\log \max a_i) 次,因为当 k>maxaik > \max a_i 时必然有 S=n|S| = n
    • 每组数据时间复杂度 O(nlogmaxai)O(n \log \max a_i),完全满足 n100,maxai1017n \le 100, \max a_i \le 10^{17} 的限制。

    正确性证明

    引理 1:若 f(k)=1f(k)=1,则 f(2k){1,2}f(2k) \in \{1,2\}
    证明:如前所述,所有 aimodk=xa_i \bmod k = x,则 aimod(2k)a_i \bmod (2k) 只能是 xxx+kx+k,因此至多两种。

    引理 2:存在 mm 使得 f(2m)=1f(2^m)=1f(2m+1)=2f(2^{m+1})=2
    证明:当 m=0m=0k=1k=1f(1)=1f(1)=1。当 mm 足够大使得 2m>maxai2^m > \max a_i 时,aimod2m=aia_i \bmod 2^m = a_i,由于 aia_i 互不相同,f(2m)=n2f(2^m)=n \ge 2。因此从 m=0m=0mm 足够大的过程中,f(2m)f(2^m)11 变为 2\ge 2。由引理 1,每次翻倍 ff 要么不变要么从 11 变成 22,所以必然存在某个 mm 使得 f(2m)=1f(2^m)=1f(2m+1)=2f(2^{m+1})=2。此时 k=2m+1k=2^{m+1} 即为所求。

    算法正确性:我们从 k=2k=2 开始,若 f(2)=2f(2)=2 则直接输出;否则 f(2)=1f(2)=1,此时继续翻倍。根据上述论证,一定会在某个 k=2jk=2^j 时出现 f(k)=2f(k)=2,因此算法总能找到合法解。


    参考代码

    #include <bits/stdc++.h>
    using namespace std;
    
    int main() {
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            vector<long long> a(n);
            for (int i = 0; i < n; i++) {
                cin >> a[i];
            }
            long long k = 2;
            while (true) {
                set<long long> s;
                for (long long x : a) {
                    s.insert(x % k);
                }
                if (s.size() == 2) {
                    cout << k << "\n";
                    break;
                }
                k *= 2;
            }
        }
        return 0;
    }
    

    总结

    本题的核心在于利用模数翻倍时余数的分裂性质,通过倍增法快速找到满足条件的 kk。该方法简洁高效,且符合题目要求的数据范围。

    • 1

    信息

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