题目链接
解析
直接对一个序列排序复杂度太高,但对一个(01)序列排序就很简单
假设我们认定答案就是(x),那么可以将(1)到(n)这(n)个数分成(3)部分:
[(1) a[i] < x \ (2) a[i] = x \ (3) a[i] > x
]
排序时我们只关心三部分之间的大小关系,完全可以不管((1))内部和((3))内部怎么排
再进一步合并((1))和((2)),那么如果排序后(q)位置的数在((1) cup (2))中,那么(x)就可能是答案,如果(q)位置的数在((3))中,显然(x)不是答案,答案一定大于(x)
于是可以二分这个(x),然后验证即可
验证的方法是将小于等于(x)的数看作(0),大于(x)的数看作(1),用线段树维护区间内(0)的个数,排序一个区间就先查询区间内(0)的个数(cnt),然后把(l)到(l + cnt - 1)修改为(0),(l + cnt)到(r)修改为(1),最后查询(q)位置是否为(0)即可
时间复杂度(O(n log^2 n))
代码
PS.以下代码自带大常数,洛谷5000+ms卡着过
#include <cstdio>
#include <cstring>
#include <iostream>
#define MAXN 100005
typedef long long LL;
struct SegmentTree {
int cnt0[MAXN << 2], cover[MAXN << 2];
void pushDown(int, int, int);
void build(int, int, int);
int query(int, int, int, int, int);
void update(int, int, int, int, int, int);
} tree;
int N, M, Q, op[MAXN], left[MAXN], right[MAXN];
int a[MAXN], b[MAXN];
bool check(int);
int main() {
std::ios::sync_with_stdio(false);
std::cin >> N >> M;
for (int i = 1; i <= N; ++i)
std::cin >> a[i];
for (int i = 1; i <= M; ++i)
std::cin >> op[i] >> left[i] >> right[i];
std::cin >> Q;
int l = 1, r = N;
while (l < r) {
int mid = (l + r) >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
std::cout << l << std::endl;
return 0;
}
void SegmentTree::pushDown(int id, int L, int R) {
if (cover[id] == -1) return;
int mid = (L + R) >> 1;
if (cover[id]) cnt0[id << 1] = cnt0[id << 1 | 1] = 0;
else cnt0[id << 1] = mid - L + 1, cnt0[id << 1 | 1] = R - mid;
cover[id << 1] = cover[id << 1 | 1] = cover[id];
cover[id] = -1;
}
void SegmentTree::build(int id, int L, int R) {
cover[id] = -1;
if (L == R) cnt0[id] = (b[L] == 0);
else {
int mid = (L + R) >> 1;
build(id << 1, L, mid);
build(id << 1 | 1, mid + 1, R);
cnt0[id] = cnt0[id << 1] + cnt0[id << 1 | 1];
}
}
void SegmentTree::update(int id, int L, int R, int l, int r, int tp) {
if (l > r) return;
if (L >= l && R <= r) {
cover[id] = tp;
cnt0[id] = (tp ? 0 : R - L + 1);
} else {
pushDown(id, L, R);
int mid = (L + R) >> 1;
if (l <= mid) update(id << 1, L, mid, l, r, tp);
if (r > mid) update(id << 1 | 1, mid + 1, R, l, r, tp);
cnt0[id] = cnt0[id << 1] + cnt0[id << 1 | 1];
}
}
int SegmentTree::query(int id, int L, int R, int l, int r) {
if (L >= l && R <= r) return cnt0[id];
pushDown(id, L, R);
int mid = (L + R) >> 1, res = 0;
if (l <= mid) res += query(id << 1, L, mid, l, r);
if (r > mid) res += query(id << 1 | 1, mid + 1, R, l, r);
return res;
}
bool check(int m) {
for (int i = 1; i <= N; ++i)
b[i] = (a[i] > m);
tree.build(1, 1, N);
for (int i = 1; i <= M; ++i) {
int t = tree.query(1, 1, N, left[i], right[i]);
if (op[i]) tree.update(1, 1, N, right[i] - t + 1, right[i], 0), tree.update(1, 1, N, left[i], right[i] - t, 1);
else tree.update(1, 1, N, left[i], left[i] + t - 1, 0), tree.update(1, 1, N, left[i] + t, right[i], 1);
}
return tree.query(1, 1, N, Q, Q);
}