[HEOI2016|TJOI2016]排序

题目描述

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

输入输出格式 输入格式:

输入数据的第一行为两个整数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

输出格式:

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

输入输出样例 输入样例#1:

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

输出样例#1:

5

首先,这道题只有一组查询,所以可以二分这个数的排名

每次二分一个要查询的数在序列中的大小排名

(排名指在升序的情况下的排名)

然后当val[i] >= mid 此位置就为1 反之则为0 这样一来,这道题就变成了一个01序列排序,所以就可以用线段树实现logn排序

线段树维护区间和,需要实现区间覆盖

每次排序前先查询排序一共有多少1

升序排序则将r-区间1的数量+1~r改为1 l~ r-区间1的数量改为0

最后再查询要询问的位置

因为我们是如果一个数大于等于二分的值的话就为1

所以这个位置应该是1

若要查询的位置为1,那么就增加这个数(r=mid-1)

反之,就减小这个数

由于这个数列是1~n的全排列,所以二分出的结果就是答案

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
# define ls now<<1
# define rs now<<1|1
const int M = 30005 ;
using namespace std;
inline int read(){
    char c=getchar(); int x=0,w=1;
    while(c>'9'||c<'0'){
        if(c=='-') w=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=x*10+c-'0';
        c=getchar();
    }
    return x*w;
}
struct Q{
    int opt,l,r;
}q[M];
int n,m,st[M],val[M],tag[M<<2],tree[M<<2],K;
inline void pushup(int now){
    tree[now]=tree[ls]+tree[rs];
}
void build(int l,int r,int now){
    tag[now]=-1;
    if(l==r){
        tree[now]=st[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,ls); build(mid+1,r,rs);
    pushup(now);
}
inline void pushdown(int now,int l,int r){
    if(tag[now]<0) return ;
    tag[ls]=tag[rs]=tag[now];
    tree[ls]=tag[now]*l; tree[rs]=tag[now]*r;
    tag[now]=-1;
}
void change(int L ,int R ,int val,int l,int r,int now){
    if(l>R||r<L) return ;
    if(l>=L&&r<=R){
        tree[now]=val*(r-l+1);
        tag[now]=val; return ;
    }
    int mid=(l+r)>>1;
    pushdown(now,mid-l+1,r-mid);
    change(L,R,val,l,mid,ls);
    change(L,R,val,mid+1,r,rs);
    pushup(now);
}
int query(int L, int R,int l,int r,int now){
    if(l>R||r<L) return 0;
    if(l>=L&&r<=R) return tree[now];
    int mid=(l+r)>>1;
    pushdown(now,mid-l+1,r-mid);
    int Ans=0;
    Ans+=query(L, R, l,mid,ls);
    Ans+=query(L, R, mid+1,r,rs);
    return Ans;
}
inline int judge(int mid){
    for(register int i=1;i<=n;++i)
      if(val[i]>=mid) st[i]=1;
      else st[i]=0;
    build(1,n,1);
    for(register int i=1;i<=m;++i){
        int l =q[i].l, r= q[i].r ;
        if(q[i].opt==0){
        // 升序排列 
            int num1=query(l,r,1,n,1);
            change(r-num1+1,r,1,1,n,1);
            change(l,r-num1,0,1,n,1);
        }
        else{
        //  降序排列
            int num1=query(l,r,1,n,1);
            change(l,l+num1-1,1,1,n,1);
            change(l+num1,r,0,1,n,1);
        }
    }
    int tmp=query(K,K,1,n,1);
    return tmp;
}
int main(){
    n=read(); m=read();
    for(register int i=1;i<=n;++i) val[i]=read();
    for(register int i=1;i<=m;++i){
        q[i].opt=read(); q[i].l=read(); q[i].r=read();
    }
    K = read() ;
    int L=1 , R=n , Ans=0;
    while(L<=R){
        int mid=(L+R)>>1;
        if(judge(mid))  L=mid+1,Ans=mid ;
        else  R=mid-1;
    }
    printf("%d\n",Ans);
    return 0;
}