P2824排序(线段树+二分)

传送门

题意:给我们一个n的全排列,让我们先后对一些区间进行升降序排序,最后输出某个位置的值。

思路:嗯,区间,是线段树的的味道哒! 最开始想的是标记每个区间操作后的升序降序情况,

如果这次处理的区间是之前操作过的子区间,也就是这次操作的区间本身就是升序

或者降序的,如果和本次操作方向相同,则不做操作,如果相反,则置换更改,复杂度O(n/2),比

暴力O(n*logn)好些,然后如果这次处理的区间不是规则的,则暴力排序,但是只水了50分...

正解是二分或者可分裂合并值域线段树=_=,果断看二分,我们二分最终的结果x,

然后把序列中大于等于x的数标记为1,小于x的数标记为0,建线段树,然后再对进行所有排序操作,

假如是升序的话,我们先求出操作区间中1的个数cnt,然后把(R[i]-cnt+1,R[i])区间标记为1,把

(L[i],R[i]-cnt)区间标记为0就完成了排序,降序同理,最后查询一下结果位置的值是否为1,如果为1,

说明x是可取的,因为我们是对原序列中值>=x的位置取为了1,...想了一下,我也不是很清楚为啥+-+,不管了

反正以后也不会遇到,爬了爬了。

题解:

这个二分成立因为是满足单调性的:可以简单地假设一下,如果你二分的答案是1,那么原序列所有的值都转化为了1,所以最后肯定是true。

如果二分一个值成立当且仅当这个位子的值大于等于mid,故如果check返回true,则l = mid+1,否则r = mid-1。

AC代码:

#include<cstdio>
#include<cstring>
#include<cctype>
#define lc o<<1
#define rc o<<1|1
#define mid (l+r)/2
using namespace std;

const int N = 100010;
int n, m, p;
int T[4*N], lazy[4*N];//segment tree
int a[N], ch[N], L[N], R[N];//the information by reading

inline int read()
{
    char ch = getchar(); int x = 0;
    while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)){ x = x*10+ch-'0'; ch = getchar(); }
    return x;
}

inline void build(int o, int l, int r, int x)
{
    if (l == r){
        T[o] = a[l] >= x;
        lazy[o] = 0;
        return;
    }
    build(lc, l, mid, x); build(rc, mid+1, r, x);
    T[o] = T[lc]+T[rc]; lazy[o] = 0;
}

inline void pushdown(int o, int l, int r)
{
    if (!lazy[o]) return;
    lazy[lc] = lazy[rc] = lazy[o];
    if (lazy[o] == 1){
        T[lc] = mid-l+1; T[rc] = r-mid;
    } else T[lc] = T[rc] = 0;
    lazy[o] = 0;
}

inline int query(int o, int l, int r, int x, int y)
{
    if (x <= l && y >= r) return T[o];
    if (x > r || y < l) return 0;
    pushdown(o, l, r);
    return query(lc, l, mid, x, y) + query(rc, mid+1, r, x, y);
}

inline int queryPoint(int o, int l, int r, int x)
{
    if (l == x && r == x) return T[o];
    pushdown(o, l, r);
    if (x <= mid) return queryPoint(lc, l, mid, x);
    else return queryPoint(rc, mid+1, r, x);
}

inline void update(int o, int l, int r, int x, int y, int val)
{
    if (x <= l && y >= r){
        T[o] = val*(r-l+1); lazy[o] = val ? 1 : -1;
        return;
    }
    if (x > r || y < l) return;
    pushdown(o, l, r);
    update(lc, l, mid, x, y, val);
    update(rc, mid+1, r, x, y, val);
    T[o] = T[lc]+T[rc];
}

inline bool check(int x)
{
    build(1, 1, n, x);
    for (int i = 1; i <= m; i ++){
        int cnt1 = query(1, 1, n, L[i], R[i]);
        if (ch[i] == 0){
            update(1, 1, n, R[i]-cnt1+1, R[i], 1);
            update(1, 1, n, L[i], R[i]-cnt1, 0);
        }
        else{
            update(1, 1, n, L[i], L[i]+cnt1-1, 1);
            update(1, 1, n, L[i]+cnt1, R[i], 0);
        }
    }
    return queryPoint(1, 1, n, p);
}

int main()
{
    n = read(); m = read();
    for (int i = 1; i <= n; i ++) a[i] = read();
    for (int i = 1; i <= m; i ++){
        ch[i] = read(); L[i] = read(); R[i] = read();
    }
    p = read();
    int ll = 1, rr = n, midd, ans;
    while (ll <= rr){
        midd = (ll+rr) >> 1;
        if (check(midd)) ans = midd, ll = midd+1; else rr = midd-1;
    }
    printf("%d\n", ans);
    return 0;
}

 

posted @ 2021-04-10 10:01  cono奇犽哒  阅读(67)  评论(0)    收藏  举报