zoukankan      html  css  js  c++  java
  • 【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
    ", l);
        return 0;
    }
  • 相关阅读:
    sql 临时表循环更新月租金
    董事长审核租金异常处理备份
    datetable导出成Excel
    DateTable导出添加时间段
    button 美化
    JS计算两日期之间相差的月份
    刚做的JS,备份一下(空代表格计算)
    Windows 框架基础开发流程
    照片切换
    Sql datetime类型数据默认1900
  • 原文地址:https://www.cnblogs.com/gwj1314/p/9444858.html
Copyright © 2011-2022 走看看