Luogu 2824 [HEOI2016/TJOI2016]排序

BZOJ 4552

挺妙的解法。

听说这题直接用一个桶能拿到$80 \ pts$

发现如果是一个排列的话,要对这个序列排序并不好做,但是假如是$01$序列的话,要对一个区间排序还是很简单的。

发现最后的询问其实只有一个,所以我们考虑二分这个答案(其实感觉在这题中答案的单调性并不是很明显),每一次二分得到一个$mid$,对于所有的$a_i$,我们把$a_i \geq mid$的$i$都记为$1$,把所有$a_i < mid$的值都记为$0$,然后对于每一次排序,我们只要获取这个区间的$0$和$1$的个数,然后区间覆盖一下就可以了。

区间覆盖,区间求和,岂不是线段树。

这样子所有操作完成了之后,我们只要看一看原来询问的这个位置的数是不是$1$,如果是$1$,那么说明这里的数$\geq mid$,移动左端点,否则移动右端点。

答案的单调性在这个时候就显现出来了。

这个思想值得借鉴。

时间复杂度$O(nlog^2n)$。

Code:

#include <cstdio>
#include <cstring>
using namespace std;

const int N = 1e5 + 5;

int n, m, K, a[N], b[N];

struct Option {
    int type, x, y;
} q[N];  

inline void read(int &X) {
    X = 0; char ch = 0; int op = 1;
    for(; ch > '9'|| ch < '0'; ch = getchar())
        if(ch == '-') op = -1;
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        X = (X << 3) + (X << 1) + ch - 48;
    X *= op;
}

namespace SegT {
    int s[N << 2], tag[N << 2];
    
    #define lc p << 1
    #define rc p << 1 | 1
    #define mid ((l + r) >> 1)
    
    inline void up(int p) {
        if(p) s[p] = s[lc] + s[rc];
    }
    
    inline void down(int p, int l, int r) {
        if(tag[p] == -1) return;
        s[lc] = tag[p] * (mid - l + 1), tag[lc] = tag[p];
        s[rc] = tag[p] * (r - mid), tag[rc] = tag[p];
        tag[p] = -1;
    }
    
    void build(int p, int l, int r) {
        tag[p] = -1;
        if(l == r) {
            s[p] = b[l];
            return;
        }
        
        build(lc, l, mid);
        build(rc, mid + 1, r);
        up(p);
    }
    
    void modify(int p, int l, int r, int x, int y, int v) {
        if(x <= l && y >= r) {
            s[p] = (r - l + 1) * v;
            tag[p] = v;
            return;
        }
        
        down(p, l, r);
        if(x <= mid) modify(lc, l, mid, x, y, v);
        if(y > mid) modify(rc, mid + 1, r, x, y, v);
        up(p);
    }
    
    int query(int p, int l, int r, int x, int y) {
        if(x <= l && y >= r) return s[p];
        
        down(p, l, r);
        
        int res = 0;
        if(x <= mid) res += query(lc, l, mid, x, y);
        if(y > mid) res += query(rc, mid + 1, r, x, y);
        return res;
    }
    
    #undef lc
    #undef rc
    #undef mid
    
} using namespace SegT;

inline bool chk(int mid) {
    for(int i = 1; i <= n; i++) 
        if(mid <= a[i]) b[i] = 1;
        else b[i] = 0;
    
    build(1, 1, n);
    for(int i = 1; i <= m; i++) {            
        int num1 = query(1, 1, n, q[i].x, q[i].y);
        int num0 = q[i].y - q[i].x + 1 - num1;
        if(!q[i].type) {
            if(num0) modify(1, 1, n, q[i].x, q[i].x + num0 - 1, 0);
            modify(1, 1, n, q[i].x + num0, q[i].y, 1); 
        } else {
            if(num1) modify(1, 1, n, q[i].x, q[i].x + num1 - 1, 1);
            modify(1, 1, n, q[i].x + num1, q[i].y, 0); 
        }  
    }
    
    int res = query(1, 1, n, K, K);
    return (res > 0);
}

int main() {
    read(n), read(m);
    for(int i = 1; i <= n; i++) read(a[i]);
    for(int i = 1; i <= m; i++)
        read(q[i].type), read(q[i].x), read(q[i].y);
    read(K);
    
    int ln = 1, rn = n, mid, res;
    for(; ln <= rn; ) {
        mid = (ln + rn) / 2;
        if(chk(mid)) ln = mid + 1, res = mid;
        else rn = mid - 1;
    }
    
    printf("%d\n", res);
    return 0;
}
View Code

 

posted @ 2018-09-25 20:59  CzxingcHen  阅读(136)  评论(0编辑  收藏  举报