BZOJ4552 [Tjoi2016&Heoi2016]排序

1. Description

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

2. Input

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

3. Output

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

4. Sample Input

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

5. Sample Output

5

5. 思路

离线询问之后二分答案。使用线段树维护,将大于等于二分的$mid$值的数视为1,否则为0,这样查询的序列就变成了一个01序列。此时一个区间被排序后会是一段全是0一段全是1的状态,因此排序只需要查询区间的1的个数(这里也就是区间求和)然后进行区间修改就可以了。最后判断一下$q$位置的值是否为1(不然就小于$mid$了),继续二分直到得到答案。

6. 代码

主要来自http://blog.csdn.net/werkeytom_ftd/article/details/51366237

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <cctype>
#include <cmath>
#include <algorithm>
#define the_best_pony "Rainbow Dash"

using namespace std;
const int maxn=100000+10;

int a[maxn],sum[maxn*5],set[maxn*5],ask[maxn][3];
bool bz[maxn*5];
int i,j,k,l,r,mid,t,n,m,p;

void mark(int p,int l,int r,int v){
    sum[p]=(v?0:r-l+1);
    set[p]=v;
    bz[p]=1;
    return;
}

void down(int p,int l,int r){
    int mid=(l+r)/2;
    if(bz[p]){
        mark(p*2,l,mid,set[p]);
        mark(p*2+1,mid+1,r,set[p]);
        bz[p]=0;
    }
    return;
}

void change(int p,int l,int r,int a,int b,int v){
    if(a>b) return;
    if(l==a&&r==b){
        mark(p,l,r,v);
        return;
    }
    down(p,l,r);
    int mid=(l+r)/2;
    if(b<=mid) change(p*2,l,mid,a,b,v);
    else if(a>mid) change(p*2+1,mid+1,r,a,b,v);
    else change(p*2,l,mid,a,mid,v),change(p*2+1,mid+1,r,mid+1,b,v);
    sum[p]=sum[p*2]+sum[p*2+1];
    return;
}

int query(int p,int l,int r,int a,int b){
    if(l==a&&r==b) return sum[p];
    down(p,l,r);
    int mid=(l+r)/2;
    if(b<=mid) return query(p*2,l,mid,a,b);
    else if(a>mid) return query(p*2+1,mid+1,r,a,b);
    else return query(p*2,l,mid,a,mid)+query(p*2+1,mid+1,r,mid+1,b);
}

bool check(int x){
    for(int i=1;i<=n;i++) 
        if(a[i]<x) change(1,1,n,i,i,0);
		else change(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]){
            change(1,1,n,ask[i][1],ask[i][2]-t,1);
            change(1,1,n,ask[i][2]-t+1,ask[i][2],0);
        }
        else{
            change(1,1,n,ask[i][1],ask[i][1]+t-1,0);
            change(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);
    l=1;r=n;
    while(l<r){
        mid=(l+r+1)/2;
        if(check(mid)) l=mid;
		else r=mid-1;
    }
    printf("%d\n",l);
    return 0;
}

 

posted @ 2017-09-16 09:53  VKorpela  阅读(166)  评论(0编辑  收藏  举报
知识共享许可协议
爱与包容记心中 · 友谊魔法永流传
加密文章的密码是最棒的小马名字(没有空格)