CF1873F

发布时间 2023-09-22 11:09:26作者: yxu0528

二分+前缀和。

要求使得区间和小于 \(k\) 的子区间长度,显然可以二分处理。二分区间长度,枚举区间左端点,check 两项内容:区间是否合法(符合 \(h_i \mod h_{i+1}=0\) ),区间和是否小于 \(k\)。对于当前区间长度,只要有一个区间满足条件,即返回真。

区间和可以通过前缀和 \(O(1)\) 的计算,而区间的合法性则可以记录每个 \(i\) 到自身所属的最大合法段右端点的距离来判断,总时间复杂度 \(O(n\log n)\)

下面是 AC 代码:

inline bool check(vector<int>& a, vector<int>& len, int n, int k, int x) {
    for (int i = 1; i <= n; i++) {
        if (i + x - 1 <= i + len[i] && a[i + x - 1] - a[i - 1] <= k) {
            return true;
        }
    }
    return false;
}

void solve() {
    //输入部分省略
    vector<int> len(n + 1);
    for (int i = n - 1; i >= 1; i--) {
        if (h[i] % h[i + 1] == 0) {
            len[i] = len[i + 1] + 1; //i 到自身最大合法段右端点的距离
        }
    }
    int l = 0, r = n;
    while (l < r) {
        int mid = (l + r + 1) >> 1; //都试一下,RE 就换另一个
        if (check(a, len, n, k, mid)) {
            l = mid;
        } else {
            r = mid - 1;
        }
    }
    cout << l << endl;
}

近期做题的几点教训:

  1. 明确变量的含义,到底是“到末尾的距离”还是“区间的长度”,加 1 减 1 的失误往往会导致惨痛后果;
  2. 二分 while 循环的条件始终是 \(l<r\)\(l,r\) 改变必须保证答案在答案区间内;\(mid\)\((l+r)/2\) 还是 \((l+r+1)/2\) 可以自己试一试;
  3. 明确 \(l,r\) 的初始值,也就是答案的可能范围,过小则找不到答案,过大容易造成溢出;这个问题值得花时间认真计算一下,不要想当然;
  4. 数字相乘一定想一下是否会爆 int 或 long long!

THE END