现 断 数 尚 贰 芬
尝试解决这样一个问题:给定 \(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;
}

浙公网安备 33010602011771号