[题解] TJOI2016 排序

TJOI 2016 排序

题目链接

题解

这是一道二分答案的好题(感觉藏得很深)。

题目要求我们局部排序 m m m次,然后求第 q q q个位置的值。显然如果我们暴力模拟的话时间复杂度最坏为 O ( n 2 l o g n ) O(n^2logn) O(n2logn)

那么考虑能否加快排序速度。

显然如果序列只有 0 , 1 0,1 0,1的话,我们可以利用线段树区间求和,区间赋值,做到 O ( l o g n ) O(logn) O(logn)排序一段区间。

那么如果题目为 0 , 1 0,1 0,1序列,这样的时间复杂度为 O ( m l o g n ) O(mlogn) O(mlogn)

现在序列不是 0 , 1 0,1 0,1序列,考虑二分答案的套路:二分一个 m i d mid mid,然后把 a [ i ] > = m i d a[i]>=mid a[i]>=mid的值改为 1 1 1, a [ i ] < m i d a[i]<mid a[i]<mid的值改为 0 0 0

再做一遍,看 q q q位置上是否为 1 1 1,若为 1 1 1则调整往右二分,为 0 0 0往左二分。

是否满足单调性?因为 m i d mid mid越小, 1 1 1的个数越多,那么如果二分到 x x x时第 q q q个为 1 1 1,那么二分比 x x x小的数第 q q q个位置上一样是 1 1 1

二分比 x x x大的数则不一定,所以满足单调性。

C o d e \mathcal{Code} Code

/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年10月14日 星期一 16时30分00秒
*******************************/
#include<iostream>
#include<cstdio>

using namespace std;

const int maxn=1e5+10;
int n,m,k,l[maxn],r[maxn],opt[maxn],p[maxn],a[maxn];

/*{{{线段树*/
namespace SegmentTree{

    int tr[maxn*4],lazy[maxn*4];//tr[k]表示区间下1的个数

    void update(int k) { tr[k]=tr[k<<1]+tr[k<<1|1]; }

    void add(int k,int l,int r,int val)
    {
        tr[k]=(r-l+1)*val;
        lazy[k]=val;
    }

    void pushdown(int k,int l,int r,int mid)
    {
        if(lazy[k]==-1) return;
        add(k<<1,l,mid,lazy[k]);
        add(k<<1|1,mid+1,r,lazy[k]);
        lazy[k]=-1;
    }

    void build(int k,int l,int r)
    {
        if(l==r)
        {
            tr[k]=a[l];
            return;
        }
        int mid=(l+r)>>1;
        build(k<<1,l,mid);
        build(k<<1|1,mid+1,r);
        lazy[k]=-1;
        update(k);
    }

    void modify(int k,int l,int r,int x,int y,int val)
    {
        if(l>=x && r<=y) return add(k,l,r,val);
        if(l>y  || r<x ) return;
        int mid=(l+r)>>1;
        pushdown(k,l,r,mid);
        modify(k<<1,l,mid,x,y,val); modify(k<<1|1,mid+1,r,x,y,val);
        update(k);
    }

    int query(int k,int l,int r,int x,int y)
    {
        if(l>=x && r<=y) return tr[k];
        if(l>y  ||  r<x) return 0;
        int mid=(l+r)>>1;
        pushdown(k,l,r,mid);
        return (query(k<<1,l,mid,x,y)+query(k<<1|1,mid+1,r,x,y));
    }

};

/*}}}*/

bool check(int mid)
{
    for(int i=1;i<=n;i++)
        a[i]=(p[i]>=mid);
    SegmentTree::build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int s=SegmentTree::query(1,1,n,l[i],r[i]),len=r[i]-l[i];
        if(opt[i]==0)
        {
            SegmentTree::modify(1,1,n,l[i],l[i]+len-s,0);
            SegmentTree::modify(1,1,n,l[i]+len-s+1,r[i],1);
        }
        else
        {
            SegmentTree::modify(1,1,n,l[i],l[i]+s-1,1);
            SegmentTree::modify(1,1,n,l[i]+s,r[i],0);
        }
    }
    return SegmentTree::query(1,1,n,k,k);
}

int main()
{
    /*freopen("p2824.in","r",stdin);*/
    /*freopen("p2824.out","w",stdout);*/
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>p[i];
    for(int i=1;i<=m;i++)
        cin>>opt[i]>>l[i]>>r[i];
    cin>>k;
    int z=1,y=n,mid,ans;
    while(z<=y)
    {
        mid=(z+y)>>1;
        if(check(mid))
            ans=mid,z=mid+1;
        else
            y=mid-1;
    }
    cout<<ans<<endl;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值