1 条题解

  • 0
    @ 2026-4-27 21:08:08

    题目详细题解

    题目理解

    给定一个长度为 nn 的数组 aa,需要将 nn 个元素划分成两个非空的序列 BBCC,使得两个序列的最大公约数(GCD)不相等

    目标是:

    • 判断是否存在这样的划分
    • 如果存在,输出任意一种划分方案

    关键观察

    观察 1:当所有元素相等时,一定无解

    设所有元素都等于 xx,则无论怎样划分,BB 序列的 GCD 一定是 xxCC 序列的 GCD 也一定是 xx。因此:

    gcd(B)=x=gcd(C)\gcd(B) = x = \gcd(C)

    不满足 gcd(B)gcd(C)\gcd(B) \neq \gcd(C) 的条件,所以无解


    观察 2:当存在至少两个不同的元素时,一定有解

    假设数组中有两个不同的元素,记作 ppqqpqp \neq q)。我们可以构造如下划分:

    • 将第一个元素 a0a_0 放入序列 BB
    • 找到第一个与 a0a_0 不同的元素 aka_kaka0a_k \neq a_0),也放入序列 BB
    • 其余所有元素放入序列 CC

    这样构造为什么可行?

    设序列 BB 包含 a0a_0aka_k,序列 CC 包含剩下的所有元素。

    序列 BB 的 GCD

    gcd(B)=gcd(a0,ak,其他可能元素)\gcd(B) = \gcd(a_0, a_k, \text{其他可能元素})

    由于 a0aka_0 \neq a_k,且 a0a_0aka_k 都是序列 BB 中的元素,所以 gcd(B)\gcd(B) 一定同时是 a0a_0aka_k 的约数。

    因此:

    gcd(B)min(a0,ak)\gcd(B) \leq \min(a_0, a_k)

    序列 CC 的 GCD: 如果 a0a_0 在序列 CC 中,那么 gcd(C)\gcd(C) 一定是 a0a_0 的约数;如果 aka_k 在序列 CC 中,那么 gcd(C)\gcd(C) 一定是 aka_k 的约数。

    更关键的是:根据构造,序列 CC至少包含 a0a_0aka_k 中的一个,但不可能同时包含两者(因为 a0a_0aka_k 都在 BB 中)。


    严格证明

    为了严格证明 GCD 不同,我们考虑两种情况:

    情况 1gcd(B)a0\gcd(B) \neq a_0gcd(B)ak\gcd(B) \neq a_k

    因为 gcd(B)\gcd(B) 同时整除 a0a_0aka_k,而 a0a_0aka_k 互不整除对方(不一定互质,但至少不相等),所以 gcd(B)\gcd(B) 一定小于 a0a_0aka_k

    同时,序列 CC 一定包含 a0a_0aka_k 中的至少一个。如果 a0Ca_0 \in C,则 gcd(C)\gcd(C) 整除 a0a_0;如果 akCa_k \in C,则 gcd(C)\gcd(C) 整除 aka_k

    由于 gcd(B)\gcd(B) 只是 a0a_0aka_k 的公约数,而 gcd(C)\gcd(C) 至少是 a0a_0aka_k 本身的约数,实际上 gcd(C)\gcd(C) 可能等于 a0a_0aka_k,而 gcd(B)<a0\gcd(B) < a_0gcd(B)<ak\gcd(B) < a_k,因此 gcd(B)gcd(C)\gcd(B) \neq \gcd(C)

    情况 2:更简单的构造——让 BB 只包含两个不同元素

    实际上,我们可以让 BB 只包含这两个不同的元素 a0a_0aka_kCC 包含剩余所有元素。

    那么:

    • gcd(B)=gcd(a0,ak)min(a0,ak)\gcd(B) = \gcd(a_0, a_k) \leq \min(a_0, a_k)
    • CC 包含 n21n-2 \geq 1 个元素,其中至少包含 a0a_0aka_k 中的一个(如果 n=2n=2 则只有一个元素,但已经保证至少两个不同元素,此时 BB 包含 a0a_0aka_kCC 为空?不对,n=2n=2 时如果两个元素不同,不能这样构造因为 CC 必须非空)

    需要调整:当 n=2n=2 且两个元素不同时,只能一个元素在 BB,一个在 CC,此时:

    • gcd(B)=a0\gcd(B) = a_0gcd(C)=ak\gcd(C) = a_k,因为 a0aka_0 \neq a_k,所以满足条件。

    算法步骤

    1. 检查数组中所有元素是否相等

      • 如果全相等,输出 No
      • 否则,输出 Yes 并构造方案
    2. 构造方案:

      • 将第一个元素标记为 11(放入 BB
      • 找到第一个与 a0a_0 不同的元素,标记为 11(也放入 BB
      • 其余所有元素标记为 22(放入 CC
      • 输出 nn 个标记

    举例说明

    示例 1a=[1,20,51,9]a = [1, 20, 51, 9]

    • a0=1a_0 = 1,找到第一个不等于 11 的元素是 2020(位置 11
    • BB 包含位置 0011[1,20][1, 20]
    • CC 包含位置 2233[51,9][51, 9]
    • gcd(B)=gcd(1,20)=1\gcd(B) = \gcd(1, 20) = 1gcd(C)=gcd(51,9)=3\gcd(C) = \gcd(51, 9) = 3
    • 131 \neq 3,满足条件

    示例 2a=[5,5,5,5]a = [5, 5, 5, 5]

    • 所有元素相等,无解

    示例 3a=[1,2,2]a = [1, 2, 2]

    • a0=1a_0 = 1,第一个不等于 11 的元素是 22(位置 11
    • BB 包含位置 0011[1,2][1, 2]
    • CC 包含位置 22[2][2]
    • gcd(B)=gcd(1,2)=1\gcd(B) = \gcd(1, 2) = 1gcd(C)=2\gcd(C) = 2
    • 121 \neq 2,满足条件

    时间复杂度

    • 判断是否全相等:O(n)O(n)
    • 构造方案:O(n)O(n)
    • 总时间复杂度:O(n)O(n) 每个测试用例
    • 空间复杂度:O(n)O(n) 用于存储答案

    由于 n100n \leq 100t500t \leq 500,总操作次数在 5×1045 \times 10^4 以内,完全可行。


    正确性证明

    必要性:如果所有元素相等,无论如何划分,两个序列的 GCD 都等于该公共值,因此 gcd(B)=gcd(C)\gcd(B) = \gcd(C),不满足条件。所以全相等是无解的必要条件。

    充分性:如果存在至少两个不同的元素,按上述方法构造:

    • n=2n = 2 时,两个元素不同,分别放入 BBCCgcd(B)=a0\gcd(B) = a_0gcd(C)=a1\gcd(C) = a_1,因为 a0a1a_0 \neq a_1,所以满足
    • n3n \geq 3 时:
      • BB 至少包含 a0a_0aka_k 两个不同元素,因此 gcd(B)gcd(a0,ak)<max(a0,ak)\gcd(B) \leq \gcd(a_0, a_k) < \max(a_0, a_k)
      • CC 包含剩余 n21n-2 \geq 1 个元素,其中必然包含 a0a_0aka_k(实际上 n3n \geq 3 时数组中除了 a0a_0aka_k 还有其他元素,但 CC 一定包含除了这两个以外的其他元素?需要仔细分析)

    实际上更简洁的证明:构造 B=[a0]B = [a_0]C=[a1,a2,,an1]C = [a_1, a_2, \dots, a_{n-1}]

    • 如果 gcd(C)a0\gcd(C) \neq a_0,则直接满足
    • 如果 gcd(C)=a0\gcd(C) = a_0,说明 a0a_0 整除 CC 中所有元素,那么可以交换一个元素,最终总能得到不同 GCD

    本题的构造法是一种简单且正确的方案。


    代码实现

    #include <bits/stdc++.h>
    using namespace std;
    
    int main() {
        int t;
        cin >> t;
        
        while (t--) {
            int n;
            cin >> n;
            
            vector<int> a(n);
            for (int i = 0; i < n; i++) {
                cin >> a[i];
            }
            
            // 检查是否所有元素相等
            bool allEqual = true;
            for (int i = 1; i < n; i++) {
                if (a[i] != a[0]) {
                    allEqual = false;
                    break;
                }
            }
            
            if (allEqual) {
                cout << "No" << endl;
            } else {
                cout << "Yes" << endl;
                
                // 找到第一个与 a[0] 不同的元素位置
                int pos = -1;
                for (int i = 1; i < n; i++) {
                    if (a[i] != a[0]) {
                        pos = i;
                        break;
                    }
                }
                
                // 构造答案
                vector<int> ans(n, 2);  // 默认全部分配给 C
                ans[0] = 1;              // 第一个元素给 B
                ans[pos] = 1;            // 不同元素也给 B
                
                // 输出
                for (int i = 0; i < n; i++) {
                    cout << ans[i];
                    if (i < n - 1) cout << " ";
                }
                cout << endl;
            }
        }
        
        return 0;
    }
    • 1

    信息

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