【TJOI2016】【bzoj4552】排序(二分答案+线段树01排序)

problem

给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序

排序分为两种
1:(0,l,r)表示将区间[l,r]的数字升序排序
2:(1,l,r)表示将区间[l,r]的数字降序排序

最后询问第q位置上的数字。

solution

考虑二分答案
对于mid
1.将所有 >= mid 的数变成 1, < mid 的数变成 0 (01排序不影响排序结果,得到相对的大小关系)
2.将处理过后的序列进行排序
如果 q 这个位置上最后是 0 ,则 ans < mid ,我们就去找更小的值
如果 q 这个位置上最后是 1, 则 ans >= mid,我们就去找更大的值

这样原题就变成了01序列排序,可以用线段树logn维护
假如说将 [l, r] 这段升序排列,
算出 sum = find(l, r) 即 [l, r] 中 1 的个数
维护排序的操作:
change(r - sum + 1, r, 1); change(l, r - sum, 0);

codes

#include<cstdio>

const int maxn = 100010;

#define lch o<<1
#define rch o<<1|1
int _a[maxn], sgt[maxn<<2], tag[maxn<<2];
void build(int o, int l, int r){
    tag[o] = -1;
    if(l == r){
        sgt[o] = _a[l];
        return ;
    }
    int mid = l+r>>1;
    build(lch,l,mid); build(rch,mid+1,r);
    sgt[o] = sgt[lch]+sgt[rch];
}
int pushdown(int o, int l, int r){
    if(tag[o] != -1){
        int mid = l+r>>1;
        sgt[lch] = (mid-l+1)*tag[o];
        sgt[rch] = (r-mid)*tag[o];
        tag[lch] = tag[rch] = tag[o];
        tag[o] = -1;
    }
}
int query(int o, int l, int r, int L, int R){
    if(r < L || l > R)return 0;
    if(L <= l && r <= R)return sgt[o];
    pushdown(o, l, r);
    int mid = l+r>>1, ans = 0;
    if(L <= mid)ans += query(lch, l, mid, L, R);
    if(R > mid)ans += query(rch, mid+1, r, L, R);
    return ans;
}
void change(int o, int l, int r, int L, int R, int v){
    if(r < L || l > R)return ;
    if(L <= l && r <= R){
        sgt[o] = (r-l+1)*v; tag[o] = v;
        return ;
    }
    pushdown(o,l,r);
    int mid = l+r>>1;
    if(L <= mid)change(lch, l, mid, L, R, v);
    if(R > mid)change(rch, mid+1, r, L, R, v);
    sgt[o] = sgt[lch]+sgt[rch];
}

int n, m, a[maxn], op[maxn], x[maxn], y[maxn], pos;
int check(int _x){
    for(int i = 1; i <= n; i++)
        _a[i] = a[i]>=_x;
    build(1,1,n);
    for(int i = 1; i <= m; i++){
        int t = query(1,1,n,x[i],y[i]);
        if(op[i])change(1,1,n,x[i],x[i]+t-1,1),change(1,1,n,x[i]+t,y[i],0);
        else change(1,1,n,x[i],y[i]-t,0), change(1,1,n,y[i]-t+1,y[i],1);
    }
    return query(1,1,n,pos,pos);
}

int main(){
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i++)scanf("%d",&a[i]);
    for(int i = 1; i <= m; i++)scanf("%d%d%d",&op[i],&x[i],&y[i]);
    scanf("%d", &pos);
    int l = 1, r = n;
    while(l < r){
        int mid = l+r+1>>1;
        if(check(mid))l = mid;
        else r = mid-1;
    }
    printf("%d\n", l);
    return 0;
}
posted @ 2018-05-18 06:09  gwj1139177410  阅读(140)  评论(0编辑  收藏  举报
选择