[bzoj4552][二分][线段树]排序

39 篇文章 0 订阅
17 篇文章 0 订阅

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

题解

神题
二分答案ans,将数列里小于等于ans的设为0,大于ans的设为1
两种操作的话就是相当于在一段里一部分改成0一部分改成1
这个用线段树来维护
最后观察q答案的时候,如果q==0,那么往小的缩范围,并记录答案
否则往大的缩范围
满足二分性

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
struct node
{
    int lc,rc,l,r;
    int z,o;bool lazy;
}tr[210000];int trlen;
void bt(int l,int r)
{
    int now=++trlen;
    tr[now].l=l;tr[now].r=r;
    tr[now].lc=tr[now].rc=-1;
    tr[now].z=tr[now].o=0;tr[now].lazy=false;
    if(l<r)
    {
        int mid=(l+r)/2;
        tr[now].lc=trlen+1;bt(l,mid);
        tr[now].rc=trlen+1;bt(mid+1,r);
    }
}
void lazy(int x)
{
    int lc=tr[x].lc,rc=tr[x].rc;
    if(tr[x].z==(tr[x].r-tr[x].l+1))
    {
        tr[lc].z=tr[lc].r-tr[lc].l+1;tr[lc].o=0;
        tr[rc].z=tr[rc].r-tr[rc].l+1;tr[rc].o=0;
        tr[lc].lazy=tr[rc].lazy=true;
    }
    else
    {
        tr[lc].o=tr[lc].r-tr[lc].l+1;tr[lc].z=0;
        tr[rc].o=tr[rc].r-tr[rc].l+1;tr[rc].z=0;
        tr[lc].lazy=tr[rc].lazy=true;
    }
    tr[x].lazy=false;
}
void ch(int now,int p,int c)
{
    if(tr[now].l==tr[now].r)
    {
        if(c==0)tr[now].z=1,tr[now].o=0;
        else tr[now].o=1,tr[now].z=0;
        return ;
    }
    int lc=tr[now].lc,rc=tr[now].rc;
    int mid=(tr[now].l+tr[now].r)/2;
    if(tr[now].lazy)lazy(now);
    if(p<=mid)ch(lc,p,c);
    else ch(rc,p,c);
    tr[now].z=tr[lc].z+tr[rc].z;
    tr[now].o=tr[lc].o+tr[rc].o;
}
int Z,O;
void findsum(int now,int l,int r)
{
    if(tr[now].l==l && tr[now].r==r)
    {
        Z+=tr[now].z;O+=tr[now].o;
        return ;
    }
    int lc=tr[now].lc,rc=tr[now].rc;
    int mid=(tr[now].l+tr[now].r)/2;
    if(tr[now].lazy)lazy(now);
    if(r<=mid)findsum(lc,l,r);
    else if(mid+1<=l)findsum(rc,l,r);
    else findsum(lc,l,mid),findsum(rc,mid+1,r);
}
void change(int now,int l,int r,int op)
{
    if(tr[now].l==l && tr[now].r==r)
    {
        if(op==0)tr[now].z=tr[now].r-tr[now].l+1,tr[now].o=0;
        else tr[now].o=tr[now].r-tr[now].l+1,tr[now].z=0;
        tr[now].lazy=true;
        return ;
    }
    int lc=tr[now].lc,rc=tr[now].rc;
    int mid=(tr[now].l+tr[now].r)/2;
    if(tr[now].lazy)lazy(now);
    if(r<=mid)change(lc,l,r,op);
    else if(mid+1<=l)change(rc,l,r,op);
    else change(lc,l,mid,op),change(rc,mid+1,r,op);
    tr[now].z=tr[lc].z+tr[rc].z;
    tr[now].o=tr[lc].o+tr[rc].o;
}
int fd(int now,int p)
{
    if(tr[now].l==tr[now].r)
    {
        if(tr[now].z==0)return 1;
        return 0;
    }
    int lc=tr[now].lc,rc=tr[now].rc;
    int mid=(tr[now].l+tr[now].r)/2;
    if(tr[now].lazy)lazy(now);
    if(p<=mid)return fd(lc,p);
    else return fd(rc,p);
}
struct ask
{
    int l,r,op;
}A[110000];
int n,m,a[110000],q;
bool check(int p)
{
    for(int i=1;i<=n;i++)
    {
        if(a[i]>p)ch(1,i,1);
        else ch(1,i,0);
    }
    for(int i=1;i<=m;i++)
    {
        Z=O=0;findsum(1,A[i].l,A[i].r);
        if(A[i].op==0)
        {
            if(Z>0)change(1,A[i].l,A[i].l+Z-1,0);
            if(Z<A[i].r-A[i].l+1)change(1,A[i].l+Z,A[i].r,1);
        }
        else
        {
            if(O>0)change(1,A[i].l,A[i].l+O-1,1);
            if(O<A[i].r-A[i].l+1)change(1,A[i].l+O,A[i].r,0);
        }
    }
    int col=fd(1,q);
    if(col==0)return true;
    return false;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    trlen=0;bt(1,n);
    for(int i=1;i<=m;i++)scanf("%d%d%d",&A[i].op,&A[i].l,&A[i].r);
    scanf("%d",&q);
    int l=1,r=n,ans;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(check(mid)){ans=mid;r=mid-1;}
        else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值