[TJOI&HEOI2016]排序

博客讨论了一道关于排序操作的问题,其中涉及对排列进行多次升序或降序排序的询问。作者指出,直接排序不是有效策略,而是提出采用二分答案的方法来判断经过排序后第p个位置的值。通过线段树解决区间计数问题,实现了O(nlog2n)的时间复杂度解决方案。
摘要由CSDN通过智能技术生成

题目大意

有一个n的排列,进行m次操作,每次操作是将一个区间升序或降序排序。
请你输出m次操作后第p个位置的值。

数据范围

n,m≤100000

分析

直接排序肯定不好做。
但是如果考虑二分答案,那么问题就变成判断最后第p个位置的值是否小于等于一个数了。
所以对于当前二分的答案mid,把a[i]≤mid的都赋值为1,a[i]>mid的赋为0。以升序排序为例,求出区间有多少个1,假设共有cnt个,那么把这些1放在区间左边cnt个位置,右边的位置全部放0。这部分用线段树解决。

时间复杂度 O(nlog2n)

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn=100005,maxt=262205;

int n,m,a[maxn],q,l,r,mid,L[maxn],R[maxn],typ[maxn],t[maxt],mask[maxt];

void insert(int l,int r,int a,int b,int v,int x)
{
    if (l==a && r==b)
    {
        mask[x]=v;
        t[x]=(r-l+1)*mask[x];
        return;
    }
    int mid=(l+r)/2;
    if (mask[x]>=0)
    {
        mask[x*2]=mask[x]; mask[x*2+1]=mask[x];
        t[x*2]=mask[x]*(mid-l+1); t[x*2+1]=mask[x]*(r-mid);
        mask[x]=-1;
    }
    if (b<=mid) insert(l,mid,a,b,v,x*2);
    else if (a>mid) insert(mid+1,r,a,b,v,x*2+1);
    else
    {
        insert(l,mid,a,mid,v,x*2); insert(mid+1,r,mid+1,b,v,x*2+1);
    }
    t[x]=t[x*2]+t[x*2+1];
}

int getsum(int l,int r,int a,int b,int x)
{
    if (l==a && r==b) return t[x];
    int mid=(l+r)/2;
    if (mask[x]>=0)
    {
        mask[x*2]=mask[x]; mask[x*2+1]=mask[x];
        t[x*2]=mask[x]*(mid-l+1); t[x*2+1]=mask[x]*(r-mid);
        mask[x]=-1;
    }
    if (b<=mid) return getsum(l,mid,a,b,x*2);
    if (a>mid) return getsum(mid+1,r,a,b,x*2+1);
    return getsum(l,mid,a,mid,x*2)+getsum(mid+1,r,mid+1,b,x*2+1);
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=0;i<m;i++) scanf("%d%d%d",&typ[i],&L[i],&R[i]);
    scanf("%d",&q);
    for (l=1,r=n,mid=(l+r)/2;l<r;mid=(l+r)/2)
    {
        memset(t,0,sizeof(t));
        memset(mask,255,sizeof(mask));
        for (int i=1;i<=n;i++) insert(1,n,i,i,(a[i]<=mid),1);
        for (int i=0;i<m;i++)
        {
            int cnt=getsum(1,n,L[i],R[i],1);
            if (!typ[i])
            {
                if (cnt) insert(1,n,L[i],L[i]+cnt-1,1,1);
                if (cnt<R[i]-L[i]+1) insert(1,n,L[i]+cnt,R[i],0,1);
            }else
            {
                if (cnt) insert(1,n,R[i]-cnt+1,R[i],1,1);
                if (cnt<R[i]-L[i]+1) insert(1,n,L[i],R[i]-cnt,0,1);
            }
        }
        if (getsum(1,n,q,q,1)) r=mid;else l=mid+1;
    }
    printf("%d\n",l);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值