Description
Hint
\(1\le n,m\le 10^5, q\in[1,n]\)
Solution
如果没有头绪的话,不妨思考一下对于 \(01\) 序列怎么做区间排序操作。
\(\texttt{00000111} \leftarrow \texttt{01010001} \rightarrow \texttt{11100000}\)
这个所谓的排序,只不过就是先数一数这段区间所有的 \(1\) 有几个 \(^{[1]}\),然后将这些 \(1\) 堆到整个区间的前面或后面 \(^{[2]}\) 。
换句话说……
- \([1] \rightarrow\) 区间求和
- \([2] \rightarrow\) 区间赋值
于是成了线段树裸题。
那么我们考虑一下如何把这个神奇的方法应用到这一题上。
假如我们选定了原序列中的一个数 \(t\),那我们要怎么知道这个数是不是答案?
我们记原序列为 \(a\) ,并定义序列 \(b\) : \(b_i = [t \le a_i]\) ,即比这个数小的记为 \(0\) ,其他的记为 \(1\)。
然后模拟一遍上述的过程。
最后,如果要求的这一位为 \(1\) ,那么真正的答案 \(\text{ans} \ge t\),反之则 \(\text{ans} < t\)。
按这个套路显然可以二分优化一下。时间复杂度 \(O(n\log^2 n)\) 。
Code
#include <iostream>
using namespace std;
const int N = 2e5 + 5;
int sum[N << 2], tag[N << 2];
int L[N << 2], R[N << 2];
#define mid ((L[rt] + R[rt]) >> 1)
#define len(rt) (R[rt] - L[rt] + 1)
inline void pushup(int rt) {
sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
inline void pushdown(int rt) {
if (~tag[rt]) {
sum[rt << 1] = tag[rt] * len(rt << 1);
sum[rt << 1 | 1] = tag[rt] * len(rt << 1 | 1);
tag[rt << 1] = tag[rt << 1 | 1] = tag[rt];
tag[rt] = -1;
}
}
void build(int *dat, int l, int r, int rt) {
L[rt] = l, R[rt] = r, tag[rt] = -1;
if (l == r) return void(sum[rt] = dat[l]);
build(dat, l, mid, rt << 1);
build(dat, mid + 1, r, rt << 1 | 1);
pushup(rt);
}
int query(int l, int r, int rt) {
if (l <= L[rt] && R[rt] <= r) return sum[rt];
pushdown(rt);
int ret = 0;
if (l <= mid) ret += query(l, r, rt << 1);
if (r > mid) ret += query(l, r, rt << 1 | 1);
return ret;
}
void update(int l, int r, int c, int rt) {
if (l <= L[rt] && R[rt] <= r) {
sum[rt] = c * len(rt);
tag[rt] = c; return;
}
pushdown(rt);
if (l <= mid) update(l, r, c, rt << 1);
if (r > mid) update(l, r, c, rt << 1 | 1);
pushup(rt);
}
#undef mid
#undef len
int n, q, k;
struct sortCmd {
int type;
int l, r;
} opt[N];
int ary[N], rec[N];
bool judge(int test) {
for (register int i = 1; i <= n; i ++)
rec[i] = int(ary[i] >= test);
build(rec, 1, n, 1);
for (register int i = 1; i <= q; i ++) {
int l = opt[i].l, r = opt[i].r;
int cnt = query(l, r, 1);
if (opt[i].type) {
update(l, l + cnt - 1, 1, 1);
update(l + cnt, r, 0, 1);
} else {
update(l, r - cnt, 0, 1);
update(r - cnt + 1, r, 1, 1);
}
}
return query(k, k, 1);
}
signed main() {
ios::sync_with_stdio(false);
cin >> n >> q;
for (register int i = 1; i <= n; i ++)
cin >> ary[i];
for (register int i = 1; i <= q; i ++)
cin >> opt[i].type >> opt[i].l >> opt[i].r;
cin >> k;
int ans = 0;
for (register int l = 1, r = n; l <= r;) {
int mid = (l + r) >> 1;
if (judge(mid)) ans = mid, l = mid + 1;
else r = mid - 1;
}
cout << ans << endl;
return 0;
}