zoukankan      html  css  js  c++  java
  • bzoj 4552: [Tjoi2016&Heoi2016]排序——二分+线段树

    Description

    在2016年,佳媛姐姐喜欢上了数字序列。因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题
    ,需要你来帮助他。这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排
    序分为两种:1:(0,l,r)表示将区间[l,r]的数字升序排序2:(1,l,r)表示将区间[l,r]的数字降序排序最后询问第q
    位置上的数字。

    Input

    输入数据的第一行为两个整数n和m。n表示序列的长度,m表示局部排序的次数。1 <= n, m <= 10^5第二行为n个整
    数,表示1到n的一个全排列。接下来输入m行,每一行有三个整数op, l, r, op为0代表升序排序,op为1代表降序
    排序, l, r 表示排序的区间。最后输入一个整数q,q表示排序完之后询问的位置, 1 <= q <= n。1 <= n <= 10^5
    ,1 <= m <= 10^5
     

    Output

     输出数据仅有一行,一个整数,表示按照顺序将全部的部分排序结束后第q位置上的数字。

    Sample Input

    6 3
    1 6 2 5 3 4
    0 1 4
    1 3 6
    0 2 4
    3

    Sample Output

    5
    ————————————————————————————————
    二分p位置的值,把大于mid的数改为1,小于等于mid的数改为0,
    变成01串后就可以用线段树实现排序了,像降序升序什么的操作就把1和0各堆到一边就可以辣
    排序后如果p的位置上的数为0,说明答案比mid小,如果为1,说明答案比mid大。
    至于为什么可以用二分呢
    你想如果p位置上是1,说明mid较小,v[p]>mid,所以把v[p]给标记成了1。
    如果p位置上是0,就是把v[p]<=mid,所以把v[p]标记成了0,
    但是这样还有一些大于v[p]的位置也是0,所以继续往小的地方逼近答案。
    满足单调所以就可以这么写辣
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    const int M=5e5+7;
    int read(){
        int ans=0,f=1,c=getchar();
        while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+(c-'0'); c=getchar();}
        return ans*f;
    }
    int p,n,m,v[M];
    int lx[M],rx[M],op[M];
    int L,R,mx;
    struct pos{int s,h[2];}tr[M];
    void up(int x){tr[x].s=tr[x<<1].s+tr[x<<1^1].s;}
    void down(int x,int l,int r){
        if(l==r) return ;
        int ls=x<<1,rs=x<<1^1,mid=(l+r)>>1;
        if(tr[x].h[0]){
            tr[x].h[0]=0; 
            tr[ls].h[0]=tr[rs].h[0]=1;
            tr[ls].s=tr[rs].s=0;
            tr[ls].h[1]=tr[rs].h[1]=0;
        }
        else if(tr[x].h[1]){
            tr[x].h[1]=0; tr[ls].h[1]=tr[rs].h[1]=1;
            tr[ls].s=mid-l+1; tr[rs].s=r-mid;
            tr[ls].h[0]=tr[rs].h[0]=0;
        }
    }
    void build(int x,int l,int r){
        tr[x].h[0]=tr[x].h[1]=0;
        if(l==r){
            tr[x].s=(v[l]>mx);
            return ;
        }
        int mid=(l+r)>>1;
        build(x<<1,l,mid);
        build(x<<1^1,mid+1,r);
        up(x);
    }
    void modify(int x,int l,int r,int v){
        if(L<=l&&r<=R){
            tr[x].h[v]=1;
            tr[x].h[v^1]=0;
            tr[x].s=(r-l+1)*v;
            return ;
        }
        down(x,l,r);
        int mid=(l+r)>>1;
        if(L<=mid) modify(x<<1,l,mid,v);
        if(R>mid)  modify(x<<1^1,mid+1,r,v);
        up(x);
    }
    int query(int x,int l,int r){
        if(L<=l&&r<=R) return tr[x].s;
        down(x,l,r);
        int mid=(l+r)>>1,sum=0;
        if(L<=mid) sum+=query(x<<1,l,mid);
        if(R>mid)  sum+=query(x<<1^1,mid+1,r);
        return sum;
    }
    bool check(int k){
        mx=k; build(1,1,n);
        for(int i=1;i<=m;i++){
            L=lx[i]; R=rx[i];
            int ly=query(1,1,n);
            if(op[i]){
                if(ly) L=lx[i],R=lx[i]+ly-1,modify(1,1,n,1);
                if(lx[i]+ly<=rx[i]) L=lx[i]+ly,R=rx[i],modify(1,1,n,0);
            }
            else{
                if(lx[i]<=rx[i]-ly) L=lx[i],R=rx[i]-ly,modify(1,1,n,0);
                if(ly) L=rx[i]-ly+1,R=rx[i],modify(1,1,n,1);
            }
        }
        L=R=p;
        return !query(1,1,n);
    }
    int main(){
        n=read(); m=read();
        for(int i=1;i<=n;i++) v[i]=read();
        for(int i=1;i<=m;i++) op[i]=read(),lx[i]=read(),rx[i]=read();
        p=read();
        int l=1,r=n;
        while(l<r){
            int mid=(l+r)>>1;
            if(check(mid)) r=mid;
            else l=mid+1; 
        }printf("%d
    ",l);
        return 0;
    }
    View Code
  • 相关阅读:
    巧用border效果
    移动端页面无刷新跳转方法有三种
    word-break和word-wrap的使用和区别
    动态获取移动端视宽,从而结合rem达到适配
    一步一步学习IdentityServer4 (3)自定登录界面并实现业务登录操作
    一步一步学习IdentityServer3 (15) 授权模式那些事
    一步一步学习IdentityServer4 (2) 开始一个简单的事例
    一步一步学习IdentityServer4 (1) 概要配置说明
    Owin 自定义中间件(2)中间件进阶
    一步一步学习IdentityServer3 (14) 启用Https
  • 原文地址:https://www.cnblogs.com/lyzuikeai/p/7631437.html
Copyright © 2011-2022 走看看