P2824 [HEOI2016/TJOI2016]排序

题目:P2824 [HEOI2016/TJOI2016]排序

思路:

线段树专题讲的。
讲过不只一次了,方法挺巧妙的。

这道题并不是真的需要使用线段树来做排序。
• 考虑这道题的特殊性质,发现我们只需要求最后一个元素的大小。
• 由于是全排列,我们可以直接二分答案x,将排列中大于等于x的标记为1,小于x的元素标记为0,维护一颗支持区间覆盖、查询区间和的线段树。每次排序时,查询区间中0、1的个数,之后若是升序排列将前面一段覆盖成0、后一段覆盖成1,降序排列则是反过来。
• 完成操作后,只需查询所需位置的权值即可完成二分的检验工作,总
复杂度\(O(nlog^2n)\)

在线算法先咕着,以后再填。


Code:

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m,pos,a[N],b[N];
struct data{
    int type,l,r;
}q[N];
struct Tree{
    int l,r,sum,tag;
#define l(p) (node[p].l)
#define r(p) (node[p].r)
#define sum(p) (node[p].sum)
#define tag(p) (node[p].tag)
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
}node[N<<2];
void build(int p,int l,int r){
    l(p)=l;r(p)=r;tag(p)=-1;
    if(l==r) return (void) (sum(p)=b[l]);
    int mid=(l(p)+r(p))>>1;
    build(ls(p),l,mid);
    build(rs(p),mid+1,r);
    sum(p)=sum(ls(p))+sum(rs(p));
}
void upd(int p,int v){
    tag(p)=v;
    sum(p)=(r(p)-l(p)+1)*v;
}
void pdown(int p){
    if(tag(p)!=-1){
        upd(ls(p),tag(p));
        upd(rs(p),tag(p));
        tag(p)=-1;
    }
}
int query(int p,int L,int R){
    if(l(p)>R||r(p)<L) return 0;
    if(L<=l(p)&&r(p)<=R) return sum(p);
    pdown(p);
    return query(ls(p),L,R)+query(rs(p),L,R);
}
void Set(int p,int L,int R,int v){
    if(l(p)>R||r(p)<L) return;
    if(L<=l(p)&&r(p)<=R) return upd(p,v);
    pdown(p);
    Set(ls(p),L,R,v);
    Set(rs(p),L,R,v);
    sum(p)=sum(ls(p))+sum(rs(p));
}
bool check(int mid){
    for(int i=1;i<=n;++i) b[i]=(a[i]>=mid);
    build(1,1,n);
    for(int i=1;i<=m;++i){
        int cnt=query(1,q[i].l,q[i].r);
        int len=q[i].r-q[i].l+1;
        if(q[i].type==0) Set(1,q[i].l,q[i].l+len-cnt-1,0),Set(1,q[i].l+len-cnt,q[i].r,1);
        else Set(1,q[i].l,q[i].l+cnt-1,1),Set(1,q[i].l+cnt,q[i].r,0);
    }
    return query(1,pos,pos); 
}
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",&q[i].type,&q[i].l,&q[i].r);
    scanf("%d",&pos);
    int l=1,r=n,ans=1;
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid)) ans=max(ans,mid),l=mid+1;
        else r=mid-1;
    }
    printf("%d\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/yu-xing/p/11253057.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值