【TJOI2016】【bzoj4552】排序(二分答案+線段樹01排序)

小哈里發表於2018-05-18

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;
}

相關文章