现 断 数 尚 贰 芬

尝试解决这样一个问题:给定 \(k\) 求出 \([1, n]\) 中第一个 \(x\) 使得 \(x > k\),序列带修

一个很正常的思路:我们可以使用一棵线段树,然后二分这个 \(x\) 的出现位置 \(mid\),然后线段树上求出 \([1, mid]\) 的最大值,然后根据关系调整边界。

但是这样复杂度就达到了 \(O(n \log ^2 n)\),然后为了秒掉一只 \(\log\),我们使用一个 黑 科 技 :线段树上二分。

我们还是按照普通查询来搞:当递归到 \([l, r]\) 这个区间时,我们看看这个区间的最大值有没有大于 \(k\),如果没有直接返回,否则观察这个树上节点的左右区间的最大值,如果左区间的最大值已经大于了 \(k\),那么不管右区间有没有大于 \(k\) 的都绝对不会成为答案,所以我们递归左区间,否则递归右区间,如果当前区间 \(l = r\),那么这个数就一定是答案,返回即可。

#include <iostream>

using namespace std;

const int N = 2e5 + 5;

int n, m, t[N << 2];

void update( int x, int l, int r, int p, int v ) {
  if (l > x || r < x) {
    return ;
  }
  if (l == r) {
    t[p] = v;
    return ;
  }
  int mid = l + r >> 1;
  update (x, l, mid, p << 1, v), update (x, mid + 1, r, p << 1 | 1, v);
  t[p] = max(t[p << 1], t[p << 1 | 1]);
}

int query( int l, int r, int p, int k ) {
  if (l == r) {
    return l;
  }
  int mid = l + r >> 1;
  if (t[p << 1] > k) {
    return query (l, mid, p << 1, k);
  } else {
    return query (mid + 1, r, p << 1 | 1, k);
  }
}

int main() {
  ios :: sync_with_stdio(0), cin.tie(0);
  cin >> n >> m;
  for (int i = 1, x; i <= n; ++i) {
    cin >> x, update (i, 1, n, 1, x);
  }
  for (int op, p, k; m--; ) {
    cin >> op >> p;
    if (op == 1) {
      cin >> k;
      update (p, 1, n, 1, k);
    } else {
      cout << query (1, n, 1, p) << endl;
    }
  }
  return 0;
}
posted @ 2025-03-11 21:28  Ja_Morant  阅读(32)  评论(0)    收藏  举报