loj #2055. 「TJOI / HEOI2016」排序

#2055. 「TJOI / HEOI2016」排序

 

题目描述

在 2016 年,佳媛姐姐喜欢上了数字序列。因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他。

这个难题是这样子的:给出一个 1 11 到 n nn 的全排列,现在对这个全排列序列进行 m mm 次局部排序,排序分为两种:

  1. (0,l,r) (0, l, r)(0,l,r) 表示将区间 [l,r] [l, r][l,r] 的数字升序排序
  2. (1,l,r) (1, l ,r)(1,l,r) 表示将区间 [l,r] [l, r][l,r] 的数字降序排序

排序后询问第 q qq 位置上的数字。

输入格式

输入数据的第一行为两个整数 n nn 和 m mm。n nn 表示序列的长度,m mm 表示局部排序的次数 (1≤n,m≤1051 leq n, m leq 10^51n,m105​​)。
第二行为 n nn 个整数,表示 1 11 到 n nn 的一个全排列。
接下来输入 m mm 行,每一行有三个整数 op,l,r ext{op}, l, rop,l,r, op ext{op}op 为 0 代表升序排序,op ext{op}op 为 1 代表降序排序, l,rl, rl,r 表示排序的区间。
最后输入一个整数 qqq,qqq 表示排序完之后询问的位置,1≤q≤n1 leq q leq n1qn。

输出格式

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

样例

样例输入

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

样例输出

5

二分p位置上的值

小于p的为0,大于p的为1

 线段树支持区间修改,查询区间0的个数

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100010
using namespace std;
int a[maxn],n,m;
bool cmp(int x,int y){return x>y;}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    int op,l,r;
    while(m--){
        scanf("%d%d%d",&op,&l,&r);
        if(op==1) sort(a+l,a+r+1,cmp);
        if(op==0) sort(a+l,a+r+1);
    }
    int q;scanf("%d",&q);
    cout<<a[q];
} 
80分 暴力
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 100010
using namespace std;
int a[maxn],sum[maxn*5],set[maxn*5],ask[maxn][3];
bool bz[maxn*5];
int t,n,m,p;
void mark(int k,int l,int r,int v){
    sum[k]=(v?0:r-l+1);
    set[k]=v;
    bz[k]=1;
}
void pushdown(int k,int l,int r){
    int mid=(l+r)>>1;
    if(bz[k]){
        mark(k<<1,l,mid,set[k]);
        mark(k<<1|1,mid+1,r,set[k]);
        bz[k]=0;
    }
}
void modify(int k,int l,int r,int opl,int opr,int v){
    if(opl>opr)return;
    if(l==opl&&r==opr){mark(k,l,r,v);return;}
    pushdown(k,l,r);
    int mid=(l+r)>>1;
    if(opr<=mid)modify(k<<1,l,mid,opl,opr,v);
    else if(opl>mid)modify(k<<1|1,mid+1,r,opl,opr,v);
    else modify(k<<1,l,mid,opl,mid,v),modify(k<<1|1,mid+1,r,mid+1,opr,v);
    sum[k]=sum[k<<1]+sum[k<<1|1];
}
int query(int k,int l,int r,int opl,int opr){
    if(l>=opl&&r<=opr)return sum[k];
    pushdown(k,l,r);
    int mid=(l+r)>>1,res=0;
    if(opl<=mid)res+=query(k<<1,l,mid,opl,opr);
    if(opr>mid)res+=query(k<<1|1,mid+1,r,opl,opr);
    return res;
}
bool check(int x){
    for(int i=1;i<=n;i++)
        if(a[i]<x)modify(1,1,n,i,i,0);
        else modify(1,1,n,i,i,1);
    for(int i=1;i<=m;i++){
        t=query(1,1,n,ask[i][1],ask[i][2]);
        if(ask[i][0]){
            modify(1,1,n,ask[i][1],ask[i][2]-t,1);
            modify(1,1,n,ask[i][2]-t+1,ask[i][2],0);
        }
        else {
            modify(1,1,n,ask[i][1],ask[i][1]+t-1,0);
            modify(1,1,n,ask[i][1]+t,ask[i][2],1);
        }
    }
    return !query(1,1,n,p,p);
}
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",&ask[i][0],&ask[i][1],&ask[i][2]);
    scanf("%d",&p);
    int l=1,r=n,ans=0;
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid))l=mid+1,ans=mid;
        else r=mid-1;
    }
    printf("%d",ans);
}
100分 线段树+二分答案
原文地址:https://www.cnblogs.com/thmyl/p/8914961.html