小红的数组切割

[牛客链接](https://www.nowcoder.com/practice/ddd09898956044a5bfa0995adf30ce29)

思路

贪心 + 前缀和

每个元素 的权值为 ,其中 为该元素所在块的编号。

将总权值拆成两部分:

$$

第一部分和切割无关,是个常数。关键在第二部分。

切割对块编号的影响:假设不切割,所有元素在第 1 块,此时第二部分的贡献为 。每在位置 (第 个元素之间)添加一刀,位置 及之后所有元素的块编号都加 1,总权值增量为:

$$

即从位置 到末尾的 后缀和。

我们最多切 刀(得到 块),因此只需从 个候选切割位置的增量中,贪心选取最大的 个正值累加即可。

以样例验证

  • 固定项
  • 不切割基础值
  • 三个切割位置的增量:
  • 个最大正值:
  • 答案 ,正确

代码

#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, k;
    scanf("%d%d", &n, &k);
    vector<long long> a(n);
    for (int i = 0; i < n; i++) scanf("%lld", &a[i]);
    char s[100001];
    scanf("%s", s);

    vector<int> op(n);
    for (int i = 0; i < n; i++) {
        op[i] = (s[i] == '1') ? 1 : -1;
    }

    // 基础值:所有元素在第 1 块
    long long base = 0;
    for (int i = 0; i < n; i++) {
        base += (long long)op[i] * a[i] + op[i];
    }

    // 计算每个切割位置的增量(op 的后缀和)
    vector<long long> gains;
    long long suffix = 0;
    for (int i = n - 1; i >= 1; i--) {
        suffix += op[i];
        gains.push_back(suffix);
    }

    sort(gains.rbegin(), gains.rend());

    long long ans = base;
    for (int i = 0; i < k - 1 && i < (int)gains.size(); i++) {
        if (gains[i] > 0) {
            ans += gains[i];
        } else {
            break;
        }
    }

    printf("%lld\n", ans);
    return 0;
}
import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int k = Integer.parseInt(st.nextToken());

        long[] a = new long[n];
        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < n; i++) {
            a[i] = Long.parseLong(st.nextToken());
        }

        String s = br.readLine().trim();
        int[] op = new int[n];
        for (int i = 0; i < n; i++) {
            op[i] = (s.charAt(i) == '1') ? 1 : -1;
        }

        // 基础值:所有元素在第 1 块
        long base = 0;
        for (int i = 0; i < n; i++) {
            base += (long) op[i] * a[i] + op[i];
        }

        // 计算每个切割位置的增量
        long[] gains = new long[n - 1];
        long suffix = 0;
        for (int i = n - 1; i >= 1; i--) {
            suffix += op[i];
            gains[i - 1] = suffix;
        }

        Arrays.sort(gains);

        long ans = base;
        int cnt = 0;
        for (int i = gains.length - 1; i >= 0 && cnt < k - 1; i--, cnt++) {
            if (gains[i] > 0) {
                ans += gains[i];
            } else {
                break;
            }
        }

        System.out.println(ans);
    }
}

复杂度分析

  • 时间复杂度,瓶颈在排序增量数组。
  • 空间复杂度,存储数组和增量。