题解思路

本题要求按顺序处理教室租借订单,当某个订单无法满足时(即从第 天到第 天中至少有一天的剩余教室数量不足 个),立即停止并输出该订单编号。如果所有订单均可满足,则输出 0。

优化策略

  1. 二分

    • 二分查找第一个无法满足的订单编号。假设前 个订单可以满足,则尝试更大的 ;否则,尝试更小的
    • 最终, 记录最后一个满足的订单编号。若 ,则所有订单满足;否则, 是第一个不满足的订单。
  2. 差分数组验证

    • 对于前 个订单,使用差分数组高效计算每天的总需求:
      • 初始化差分数组 (大小 ,初始为 0)。
      • 对每个订单
        • (若
      • 计算前缀和 ,得到每天的总需求。若存在 ,则前 个订单不满足。
    • 恢复差分数组:验证后恢复 为初始状态,避免影响后续验证。

复杂度分析

  • 时间复杂度
    • 二分次数
    • 每次验证 为当前订单数),最坏
  • 空间复杂度,用于存储差分数组和教室数量。

代码实现

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int MAXN = 1000010;
int n, m;
int r[MAXN];
int d[MAXN], s[MAXN], t[MAXN];
long long diff[MAXN * 2]; // 差分数组,开大避免越界

// 检查前x个订单是否满足
bool check(int x) {
    // 应用前x个订单的差分
    for (int i = 1; i <= x; i++) {
        diff[s[i]] += d[i];
        if (t[i] + 1 <= n + 1) {
            diff[t[i] + 1] -= d[i];
        }
    }

    long long cur = 0;
    bool valid = true;
    // 计算前缀和并检查每天需求
    for (int i = 1; i <= n; i++) {
        cur += diff[i];
        if (cur > r[i]) {
            valid = false;
            break;
        }
    }

    // 恢复差分数组
    for (int i = 1; i <= x; i++) {
        diff[s[i]] -= d[i];
        if (t[i] + 1 <= n + 1) {
            diff[t[i] + 1] += d[i];
        }
    }
    return valid;
}

int main() {
    memset(diff, 0, sizeof(diff));
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &r[i]);
    }
    for (int i = 1; i <= m; i++) {
        scanf("%d %d %d", &d[i], &s[i], &t[i]);
    }

    int left = 1, right = m;
    int ans = 0; // 最后一个满足的订单编号
    while (left <= right) {
        int mid = (left + right) >> 1;
        if (check(mid)) {
            ans = mid; // 前mid个订单满足
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }

    if (ans == m) {
        printf("0\n");
    } else {
        printf("-1\n%d\n", ans + 1);
    }

    return 0;
}

二分查找

  • 初始化 ,
  • 时,取中点 ,调用 验证前 个订单。
  • 若满足,更新 并搜索更大的区间;否则,搜索更小区间。

当你需要实现二分查找时,按此流程思考:

明确目标:你要找什么?(第一个≥?最后一个≤?精确匹配?)

定义条件:什么情况下 mid 是候选解?

找第一个≥target → nums[mid] >= target

找最后一个≤target → nums[mid] <= target

确定搜索方向: 候选解的更优解在哪个方向?

更小的索引 → 向左搜索 更大的索引 → 向右搜索

编写代码:

条件成立时 → 向"更优解"方向搜索

条件不成立时 → 向"可能包含解"的方向搜索

验证边界:

target小于所有元素

target大于所有元素

target等于第一个/最后一个元素

target在数组中间