小欧的数组构造

[题目链接](https://www.nowcoder.com/practice/7e86d90777ad41e4a59d2e937bf9afc2)

思路

已知长度为 的数组 ,其中 ,要求构造长度为 整数数组 ,使 中正数个数最多。

决定一切

一旦确定了 的值,整个数组就被唯一确定了:,依此类推。展开后可以发现:

$$

其中 。也就是说, 关于 是线性的,且系数在 之间交替。

转化为不等式计数

要让 (正数),分两种情况:

  • 偶数下标 ,即 (因为 是整数)
  • 奇数下标 ,即

注意偶数下标希望 尽量大,奇数下标希望 尽量小,两者互相矛盾。我们要找一个整数 ,使满足的不等式总数最多。

扫描线求解

把每个约束看成数轴上的一个事件:

  • 偶数下标 :在 处放一个 事件——当 达到这个值时,多满足一个约束
  • 奇数下标 :在 处放一个 事件——当 达到这个值时,少满足一个约束

初始时(),所有偶数约束都不满足,所有奇数约束都满足,所以初始得分 = 奇数下标的个数。

将所有事件按坐标排序,从左到右扫描并累加 delta,过程中维护得分的最大值即为答案。

样例验证

事件列表:

  • (偶):
  • (奇):
  • (偶):

排序后:。初始得分 = 1。

  • :得分 =
  • :得分 =

最大得分 = 2,与答案一致。对应 (2 个正数)。

代码

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

int main() {
    int n;
    scanf("%d", &n);
    vector<long long> b(n - 1);
    for (int i = 0; i < n - 1; i++) scanf("%lld", &b[i]);

    vector<long long> c(n);
    c[0] = 0;
    for (int i = 0; i < n - 1; i++) c[i + 1] = b[i] - c[i];

    int countOdd = 0;
    vector<pair<long long, int>> events;
    for (int i = 0; i < n; i++) {
        if (i % 2 == 0) {
            events.push_back({-c[i] + 1, +1});
        } else {
            events.push_back({c[i], -1});
            countOdd++;
        }
    }
    sort(events.begin(), events.end());

    int score = countOdd, best = score;
    int idx = 0;
    while (idx < (int)events.size()) {
        long long val = events[idx].first;
        int delta = 0;
        while (idx < (int)events.size() && events[idx].first == val) {
            delta += events[idx].second;
            idx++;
        }
        score += delta;
        best = max(best, score);
    }
    printf("%d\n", best);
    return 0;
}
import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine().trim());
        StringTokenizer st = new StringTokenizer(br.readLine().trim());
        long[] b = new long[n - 1];
        for (int i = 0; i < n - 1; i++) b[i] = Long.parseLong(st.nextToken());

        long[] c = new long[n];
        c[0] = 0;
        for (int i = 0; i < n - 1; i++) c[i + 1] = b[i] - c[i];

        int countOdd = 0;
        long[][] events = new long[n][2];
        for (int i = 0; i < n; i++) {
            if (i % 2 == 0) {
                events[i][0] = -c[i] + 1;
                events[i][1] = 1;
            } else {
                events[i][0] = c[i];
                events[i][1] = -1;
                countOdd++;
            }
        }
        Arrays.sort(events, (a, bb) -> Long.compare(a[0], bb[0]));

        int score = countOdd, best = score;
        int idx = 0;
        while (idx < n) {
            long val = events[idx][0];
            int delta = 0;
            while (idx < n && events[idx][0] == val) {
                delta += (int) events[idx][1];
                idx++;
            }
            score += delta;
            best = Math.max(best, score);
        }
        System.out.println(best);
    }
}

复杂度分析

  • 时间复杂度,瓶颈在于对事件排序。
  • 空间复杂度,存储 数组和事件数组。