1 条题解

  • 0
    @ 2026-5-14 17:55:42

    题目解析

    问题重述

    nn 条鱼按编号从小到大排列(编号越大表示鱼越大)。每条鱼被 Alice(字符 0)或 Bob(字符 1)钓到。我们需要将鱼分成 mm非空连续组,第 ii 组的每条鱼获得价值 i1i-1。设 Bob 的总价值为 BB,Alice 的总价值为 AA。要求 BAkB - A \ge k,求最小的 mm。如果不可能,输出 1-1

    核心思想

    1. 从“组边界”的角度思考

    每组鱼的价值等于它前面有多少组。换句话说,每增加一个组边界,该边界之后的所有鱼的价值都会 +1

    设第 jj 个组边界位于第 ii 条鱼之后(即第 1i1 \sim i 条鱼是前 jj 组,第 i+1ni+1 \sim n 条鱼是后面的组)。那么这个边界会让第 i+1i+1 条及之后的所有鱼的价值都增加 11

    2. 得分差的贡献

    对于第 ii 条鱼和第 i+1i+1 条鱼之间的位置,如果这里有一个组边界,那么它会给 Bob 和 Alice 的得分差贡献:

    sis_i 表示从第 ii 条鱼到第 nn 条鱼中,Bob 的鱼数减去 Alice 的鱼数。更精确地:

    $$s_i = \sum_{j=i}^{n} \begin{cases} +1 & \text{if fish } j \text{ is Bob's} \\ -1 & \text{if fish } j \text{ is Alice's} \end{cases} $$

    那么,在第 ii 条鱼之后放置一个组边界(即第 ii 条鱼和第 i+1i+1 条鱼在不同组),会对最终得分差贡献 si+1s_{i+1}

    3. 得分差的最终表达式

    设第 11 组从第 11 条鱼开始,第 22 组从第 p2p_2 条鱼开始,…,第 mm 组从第 pmp_m 条鱼开始,其中 1=p1<p2<<pmn1 = p_1 < p_2 < \dots < p_m \le n。那么 Bob 与 Alice 的得分差为:

    $$B - A = \sum_{j=1}^{m} (j-1) \cdot (\text{第 } j \text{ 组中 Bob 鱼数} - \text{Alice 鱼数}) $$

    展开后可得:

    $$B - A = 0 \cdot s_{p_1} + 1 \cdot s_{p_2} + 2 \cdot s_{p_3} + \dots + (m-1) \cdot s_{p_m} $$

    其中 spjs_{p_j} 表示从第 pjp_j 条鱼开始的差值累积和。这个表达式可以重写为:

    BA=sp2+sp3++spmB - A = s_{p_2} + s_{p_3} + \dots + s_{p_m}

    为什么?因为第 jj 组边界的贡献是 spj+1s_{p_{j+1}},而最后一组之后没有边界。实际上:

    • 11 组中的鱼价值为 00,贡献 00
    • 22 组中的鱼比第 11 组多 11 分,这个 11 分来自第 11 个组边界,贡献 sp2s_{p_2}
    • 33 组中的鱼比第 22 组多 11 分,这部分贡献 sp3s_{p_3},等等。

    所以最终:

    BA=j=2mspjB - A = \sum_{j=2}^{m} s_{p_j}

    4. 转化为选择问题

    因此,BAB - A 等于若干个 sis_i 值的和,其中 ii 是每个组边界的位置(除了第一个组没有前边界)。sis_i 是从位置 ii 到末尾的净差值。

    由于 sis_i 可以为负数,而我们的目标是让 BAkB-A \ge k,我们只关心正数的 sis_i(因为加入负数只会减小总和,对达到目标没有帮助)。

    5. 最小化组数

    组数 mm 等于“选择的 sis_i 的数量” +1+ 1(因为第一个组没有前边界)。为了用最少的组数达到目标,我们应该选择最大的 sis_i 值。

    6. 算法步骤

    1. 从右向左遍历,计算后缀差值 sis_i(Bob 为 +1+1,Alice 为 1-1
    2. 收集所有 si>0s_i > 0 的值(ii11n1n-1,因为最后一个位置之后不需要边界)
    3. 将收集的值从大到小排序
    4. 依次取最大的值累加,直到总和 k\ge k
    5. 答案 = 取的数量 +1+ 1(如果累加和 k\ge k),否则输出 1-1

    示例验证

    示例 1: n=4,k=1,s="1001"n=4, k=1, s=\text{"1001"}

    • 从右向左计算 sis_i
      • s4s_4(鱼4: Bob)=+1= +1
      • s3s_3(鱼3: Alice)=1+1=0= -1 + 1 = 0
      • s2s_2(鱼2: Alice)=1+0=1= -1 + 0 = -1
      • s1s_1(鱼1: Bob)=+1+(1)=0= +1 + (-1) = 0
    • si>0s_i > 0 的值:[1][1]
    • 111 \ge 1,取了 11 个值
    • ans=1+1=2ans = 1 + 1 = 2

    示例 3: n=4,k=1,s="0110"n=4, k=1, s=\text{"0110"}

    • 从右向左:s4=0,s3=+1,s2=+1+1=2,s1=1+2=1s_4=0, s_3=+1, s_2=+1+1=2, s_1= -1+2=1
    • si>0s_i > 0[1,2,1][1,2,1](位置 1,2,31,2,3
    • 排序:[2,1,1][2,1,1]
    • 212 \ge 1,取了 11 个值
    • ans=2ans = 2

    示例 5: n=6,k=3,s="001110"n=6, k=3, s=\text{"001110"}

    • 计算 sis_iii6611):
      • 鱼6: 01-1
      • 鱼5: 1+1+1, s5=1+1=0s_5 = -1+1=0
      • 鱼4: 1+1+1, s4=0+1=1s_4 = 0+1=1
      • 鱼3: 1+1+1, s3=1+1=2s_3 = 1+1=2
      • 鱼2: 01-1, s2=21=1s_2 = 2-1=1
      • 鱼1: 01-1, s1=11=0s_1 = 1-1=0
    • si>0s_i > 0[1,2,1][1,2,1]
    • 排序:[2,1,1][2,1,1]
    • 2<32 < 3,再取 2+1=332+1=3 \ge 3,取了 22 个值
    • ans=2+1=3ans = 2 + 1 = 3

    C++ 代码

    #include <bits/stdc++.h>
    using namespace std;
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        
        int t;
        cin >> t;
        
        while (t--) {
            int n, k;
            string s;
            cin >> n >> k >> s;
            
            vector<int> vals;
            int sum = 0;
            
            // 从右向左计算后缀差值
            for (int i = n - 1; i > 0; --i) {
                sum += (s[i] == '1' ? 1 : -1);
                if (sum > 0) {
                    vals.push_back(sum);
                }
            }
            
            // 从大到小排序
            sort(vals.begin(), vals.end(), greater<int>());
            
            int ans = 1;  // 至少有一组
            for (int x : vals) {
                if (k <= 0) break;
                k -= x;
                ++ans;
            }
            
            cout << (k > 0 ? -1 : ans) << '\n';
        }
        
        return 0;
    }
    

    复杂度分析

    • 时间复杂度:O(nlogn)O(n \log n),主要来自排序
    • 空间复杂度:O(n)O(n)
    • 所有测试用例 nn 总和 2×105\le 2 \times 10^5,完全可行

    关键点总结

    1. 组边界贡献模型:每个组边界给后面所有鱼的价值 +1
    2. 后缀差值数组sis_i 表示从位置 ii 开始的净差值
    3. 得分差 = 所选 sis_i 之和ii 为组边界位置)
    4. 贪心选择:取最大的 sis_i 以最小化组数
    5. 答案 = 选取数量 + 1(第一个组不需要前边界)
    • 1

    信息

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