[BZOJ4552][Tjoi2016&Heoi2016][线段树][二分]排序

该博客探讨了如何利用线段树解决通过特定排序网络找到序列中第k个元素的问题。采用二分答案的方法,将原序列根据比较值分为0和1,然后通过分析排序网络对区间的影响,更新线段树以找到正确位置。
摘要由CSDN通过智能技术生成
题意

求给定序列经过给定排序网络后的第k个元素


鏼爷16年论文小例题%%%

二分答案x,原数列中大于等于x的设为1,小于x的设为0。
每一次排序的区间[l,r]中,有k1个0,k2个1,那么升序就把前k1个覆盖为0,后k2个覆盖为1,降序相反。
可以用线段树维护。

#include <cstdio>
#include <iostream>
#include <algorithm>
#define N 100010

using namespace std;

int n,m,q;
int A[N],B[N];
struct stp{
  int l,r,op;
}s[N];
struct lisan{
  int x,g;
  friend bool operator <(lisan a,lisan b){return a.x<b.x;}
}L[N];
struct sigt{
  int l,r,z,o,flg;
}T[N<<2];

inline void reaD(int &x){
  char Ch=getchar();x=0;
  for(;Ch>'9'||Ch<'0';Ch=getchar());
  for(;Ch>='0'&&Ch<='9';x=x*10+Ch-'0',Ch=getchar());
}

inline void upd(int g){
  T[g].z=T[g<<1].z+T[g<<1|1].z;
  T[g].o=T[g<<1].o+T[g<<1|1].o;
}

inline void Set(int g,int w){
  if(w==1) T[g].z=T[g].r-T[g].l+1,T[g].o=0;
  else T[g].o=T[g].r-T[g].l+1,T[g].z=0;
  T[g].flg=w;
}

inline void pushdown(int g){
  if(!T[g].flg) return;
  Set(g<<1,T[g].flg);
  Set(g<<1|1,T[g].flg);
  T[g].flg=0;
}

void build(int g,int l,int r){
  T[g].l=l;T[g].r=r;T[g].flg=0;
  if(l==r){
    if(B[l]) T[g].o=1,T[g].z=0;
    else T[g].o=0,T[g].z=1;
    return ;
  }
  int mid=l+r>>1;
  build(g<<1,l,mid);
  build(g<<1|1,mid+1,r);
  upd(g);
}

int query(int g,int l,int r,int w){
  if(T[g].l==l&&T[g].r==r) return w==1?T[g].o:T[g].z;
  pushdown(g);
  int mid=T[g].l+T[g].r>>1;
  if(r<=mid) return query(g<<1,l,r,w);
  if(l>mid) return query(g<<1|1,l,r,w);
  return query(g<<1,l,mid,w)+query(g<<1|1,mid+1,r,w);
}

void cover(int g,int l,int r,int w){
  if(T[g].l==l&&T[g].r==r){Set(g,w);return;}
  pushdown(g);
  int mid=T[g].l+T[g].r>>1;
  if(r<=mid) cover(g<<1,l,r,w);
  else if(l>mid) cover(g<<1|1,l,r,w);
  else cover(g<<1,l,mid,w),cover(g<<1|1,mid+1,r,w);
  upd(g);
}

inline bool check(int x){
  for(int i=1;i<=n;i++) B[i]=A[i]>=x;
  build(1,1,n);
  for(int i=1;i<=m;i++){
    int z=query(1,s[i].l,s[i].r,0),o=s[i].r-s[i].l+1-z;
    if(!z||!o) continue;
    if(s[i].op) cover(1,s[i].l,s[i].l+o-1,2),cover(1,s[i].r-z+1,s[i].r,1);
    else cover(1,s[i].r-o+1,s[i].r,2),cover(1,s[i].l,s[i].l+z-1,1);
  }
  return query(1,q,q,1);
}

int main(){
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  reaD(n); reaD(m);
  for(int i=1;i<=n;i++) reaD(A[i]);
  for(int i=1;i<=m;i++) reaD(s[i].op),reaD(s[i].l),reaD(s[i].r);
  reaD(q); int l=1,r=n,mid,Ans;
  while(l<=r) check(mid=l+r>>1)?Ans=mid,l=mid+1:r=mid-1;
  return printf("%d\n",Ans),0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值