1 条题解

  • 0
    @ 2026-4-2 20:21:58

    题目理解

    我们有一个整数数组 a1,a2,,ana_1, a_2, \dots, a_n
    操作:选择任意 aia_i,将它改为 00aia_i 之间的整数(包含端点)。
    即:如果 ai<0a_i < 0,可改为 [ai,0][a_i, 0] 中的任意整数;
    如果 ai0a_i \ge 0,可改为 [0,ai][0, a_i] 中的任意整数。

    目标:使数组所有元素的乘积最小,并且操作次数最少
    输出最少操作次数和一种具体操作方案。


    关键观察

    1. 操作不改变符号方向

      • 如果 aia_i 是正数,只能改成 [0,ai][0, a_i],结果 0\ge 0,不会变成负数。
      • 如果 aia_i 是负数,只能改成 [ai,0][a_i, 0],结果 0\le 0,不会变成正数。
        因此,数组里每个数在操作后符号不变或变成 00
    2. 绝对值不会增大
      操作后 xai|x| \le |a_i|,所以整个数组的乘积的绝对值不会增大。

    3. 符号变化规则
      原来乘积为正 \Rightarrow 操作后乘积 0\ge 0(只能变成 00 或正数)。
      原来乘积为负 \Rightarrow 操作后乘积 0\le 0(只能变成 00 或负数)。
      原来乘积为 00 \Rightarrow 始终是 00


    最小乘积 rr 的求法

    • 如果数组中有 00,那么乘积已经为 00,这是可能的最小值(不能为负)。
      所以 r=0r = 0

    • 如果数组中没有 00,但乘积为正:
      我们可以将一个正数改成 00,乘积变为 00(比原来的正数小)。
      不能得到负数(因为所有数非负或全负?等一下——正乘积来自偶数个负数,但负数可以改成 00,所以不能变负)。
      因此 r=0r = 0

    • 如果数组中没有 00,但乘积为负:
      由于所有数都不能变符号(只能往 00 方向变,但不能变成正数),乘积的绝对值只会减小或不变,但负号保持不变。
      最小的乘积就是最负的数,但因为我们不能从负数变成正数,乘积的负号永远存在。
      那么最小乘积就是绝对值最大的负数乘积吗?不,因为绝对值可以缩小到 00,但一旦有一个数变成 00,乘积就是 00,这比负数大(0>负数0 > \text{负数}),所以负乘积不能变小成更负的数,但可以变大成 00
      因此,要使乘积最小,我们不要改成 00,因为 00 大于负数。
      那么保持乘积为负,并且绝对值尽量大?
      等等,操作只能减小绝对值,不能增大。所以初始负乘积的绝对值就是可能的最大负乘积,因此它本身已经是最小的(最负的)。
      因此 rr 就是初始乘积(负值)。


    结论(分情况讨论)

    设初始数组乘积为 PP

    情况 1:P<0P < 0

    • 操作后乘积只能 0\le 0,且不能变得更负(绝对值不增),所以 PP 已经是最小乘积。
    • 最小操作次数:00(不需要操作)。

    情况 2:P=0P = 0

    • 乘积已经是最小值 00
    • 最小操作次数:00

    情况 3:P>0P > 0

    • 可以改成 00(任选一个数改成 00),这样乘积变为 00,小于正数。
    • 最小操作次数:11(选任意一个元素改成 00)。
    • 如果某个数已经是 00,那么不用操作,但这里 P>0P > 0 意味着没有 00

    特殊情况检查

    • 如果数组全为正数,P>0P > 0,操作一次改成 00 即可。
    • 如果数组全为负数,但个数为偶数,P>0P > 0,也是操作一次改成 00
    • 如果数组有正有负,但乘积为正,同样操作一次改成 00

    例子验证

    1. [155]P>0P > 0 → 操作 1 次:改 a1a_100
    2. [2, 8, -1, 3]P=48<0P = -48 < 0 → 操作 0 次。
    3. [-1, 0, -2, -5]:乘积有 00P=0P = 0 → 操作 0 次。
    4. [-15, -75, -25, -30]:全负,4 个,偶数个 → P>0P > 0 → 操作 1 次:改任意一个为 00(比如 a3a_300)。

    输出与题目示例一致。


    算法步骤

    1. 读入 nn 和数组。
    2. 计算初始乘积 PP(注意可能溢出,但 n100n \le 100ai109|a_i| \le 10^9,乘积可能极大,所以用符号判断即可,不必算精确值):
      • 记录负数的个数 negneg,是否有 00
      • 乘积为正:neg%2=0neg \% 2 = 0 且没有 00
      • 乘积为负:neg%2=1neg \% 2 = 1 且没有 00
      • 乘积为 00:有 00
    3. 根据情况输出:
      • P<0P < 0:输出 0
      • P=0P = 0:输出 0
      • P>0P > 0:输出 1,然后输出任意一个下标 ii00

    复杂度

    O(n)O(n) 每个测试用例。


    完整题解代码框架(C++ 风格)

    #include <bits/stdc++.h>
    using namespace std;
    
    int main() {
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            vector<int> a(n);
            bool hasZero = false;
            int negCount = 0;
            for (int i = 0; i < n; i++) {
                cin >> a[i];
                if (a[i] == 0) hasZero = true;
                else if (a[i] < 0) negCount++;
            }
            // 乘积为负:奇数个负数且无0
            if (!hasZero && negCount % 2 == 1) {
                cout << "0\n";
            } 
            // 乘积为0
            else if (hasZero) {
                cout << "0\n";
            } 
            // 乘积为正
            else {
                cout << "1\n";
                // 输出任意一个元素改成0
                cout << "1 0\n";
            }
        }
        return 0;
    }
    
    • 1

    信息

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