[BZOJ4552][Tjoi2016&Heoi2016]排序(二分答案+线段树)

通过二分查找结合线段树实现高效区间排序查询。将原始序列转换为01序列,利用线段树支持区间赋值与求和操作,完成升序或降序区间排序后的数值查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先二分答案,转换为判定性问题:
询问mm次区间排序之后,q位置上的数是否mid≥mid
然后构建0101序列,对于第ii个位置,如果原序列的第i个数mid≥mid,则0101序列的第ii个数为1,否则为00
这时候,区间升序排序就相当于把一个区间内的0全部放到左边,11全部放到右边,区间降序排序相当于把一个区间内的0全部放到右边,11全部放到左边。
因此,需要写一个支持区间赋值和求区间和的线段树。
所有操作都进行完后,如果01序列的qq位置上的数为1,则原序列mm次区间排序之后q位置上的数mid≥mid
复杂度O(nlog2n)O(nlog2⁡n)
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define p2 p << 1
#define p3 p << 1 | 1
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 1e5 + 5, M = 4e5 + 5;
int n, m, a[N], op[N], l[N], r[N], q, sum[M], tag[M];
void build(int t, int l, int r, int p) {
    if (l == r) return (void) (sum[p] = a[l] >= t, tag[p] = -1);
    int mid = l + r >> 1; build(t, l, mid, p2); build(t, mid + 1, r, p3);
    sum[p] = sum[p2] + sum[p3]; tag[p] = -1;
}
void down(int p) {
    if (tag[p] != -1) tag[p2] = tag[p], tag[p3] = tag[p], tag[p] = -1;
}
void upt(int l, int r, int p) {
    int mid = l + r >> 1, ls = sum[p2], rs = sum[p3];
    if (tag[p2] != -1) ls = (mid - l + 1) * tag[p2];
    if (tag[p3] != -1) rs = (r - mid) * tag[p3]; sum[p] = ls + rs;
}
void change(int l, int r, int s, int e, int v, int p) {
    if (l == s && r == e) return (void) (tag[p] = v);
    int mid = l + r >> 1; down(p);
    if (e <= mid) change(l, mid, s, e, v, p2);
    else if (s >= mid + 1) change(mid + 1, r, s, e, v, p3);
    else change(l, mid, s, mid, v, p2), change(mid + 1, r, mid + 1, e, v, p3);
    upt(l, r, p);
}
int ask(int l, int r, int s, int e, int p) {
    if (l == s && r == e) return tag[p] == -1 ? sum[p] : tag[p] * (r - l + 1);
    int mid = l + r >> 1; down(p); int res;
    if (e <= mid) res = ask(l, mid, s, e, p2);
    else if (s >= mid + 1) res = ask(mid + 1, r, s, e, p3);
    else res = ask(l, mid, s, mid, p2) + ask(mid + 1, r, mid + 1, e, p3);
    return upt(l, r, p), res;
}
bool check(int t) {
    int i; build(t, 1, n, 1); for (i = 1; i <= m; i++) {
        int tmp = ask(1, n, l[i], r[i], 1);
        if (!tmp || tmp == r[i] - l[i] + 1) continue;
        if (op[i]) change(1, n, l[i], l[i] + tmp - 1, 1, 1),
            change(1, n, l[i] + tmp, r[i], 0, 1);
        else change(1, n, r[i] - tmp + 1, r[i], 1, 1),
            change(1, n, l[i], r[i] - tmp, 0, 1);
    }
    return ask(1, n, q, q, 1) == 1;
}
int main() {
    int i; n = read(); m = read(); for (i = 1; i <= n; i++) a[i] = read();
    for (i = 1; i <= m; i++) op[i] = read(), l[i] = read(), r[i] = read();
    q = read(); int l = 1, r = n; while (l <= r) {
        int mid = l + r >> 1; check(mid) ? l = mid + 1 : r = mid - 1;
    }
    cout << r << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值