码迷,mamicode.com
首页 > 编程语言 > 详细

[HEOI2016/TJOI2016]排序 解题报告

时间:2020-01-14 20:34:47      阅读:81      评论:0      收藏:0      [点我收藏+]

标签:另一个   一个   inline   要求   main   build   freopen   线段   代码   

[HEOI2016/TJOI2016]排序

题意

给出一个大小为 \(n\) 的排列, 对这个排列进行 \(m\) 次操作, 操作分为以下两种,

  1. 0 l r 表示将区间 \([l,r]\) 的数升序排序.
  2. 1 l r 表示将区间 \([l,r]\) 的数降序排序.

询问 \(m\) 次操作后下标为 \(q\) 的数字.


思路

不看题解打死也想不出来系列

考虑二分答案.

设当前二分的答案为 \(mid\), 把原排列中 大于等于 \(mid\) 的数标记为 \(1\), 小于 \(mid\) 的数标记为 \(0\).

对于这样的一个 \(0,1\) 串, 我们可以用线段树实现 \(\log n\) 的排序.

若当前区间 \(l,r\) 的区间和为 \(num\), 那么就代表有 \(num\)\(1\)\(r-l+1-num\)\(0\), 然后我们只需按要求把区间的前部分设为 \(0\)\(1\), 后部分设为另一个数就行了.

最后判断一下下标为 \(q\) 的数字是否为 \(1\), 若是, 则另 \(l=mid+1\), 否则另 \(r=mid-1\).


代码

#include<bits/stdc++.h>
using namespace std;
const int _=1e5+7;
const int __=4e5+7;
int n,m,q,a[_],sum[__],tag[__],ans,x;
struct oper{
  int l,r,ty;
}op[_];
void init(){
  cin>>n>>m;
  for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);
  for(int i=1;i<=m;i++)
    scanf("%d%d%d",&op[i].ty,&op[i].l,&op[i].r);
  cin>>q;
}
void build(int k,int l,int r){
  tag[k]=-1;
  if(l==r){ sum[k]= a[l]>=x; return; }
  int mid=(l+r)>>1;
  build(k<<1,l,mid);
  build(k<<1|1,mid+1,r);
  sum[k]=sum[k<<1]+sum[k<<1|1];
}
void upd(int k,int l,int r,bool w){
  sum[k]= w ?r-l+1 :0;
  tag[k]=w;
}
void psd(int k,int l,int r){
  if(tag[k]==-1) return;
  int mid=(l+r)>>1;
  upd(k<<1,l,mid,tag[k]);
  upd(k<<1|1,mid+1,r,tag[k]);
  tag[k]=-1;
}
void modify(int k,int l,int r,int x,int y,bool w){
  if(x>y) return;
  if(l>=x&&r<=y){ upd(k,l,r,w); return; }
  psd(k,l,r);
  int mid=(l+r)>>1;
  if(x<=mid) modify(k<<1,l,mid,x,y,w);
  if(y>mid) modify(k<<1|1,mid+1,r,x,y,w);
  sum[k]=sum[k<<1]+sum[k<<1|1];
}
int query(int k,int l,int r,int x,int y){
  if(l>=x&&r<=y) return sum[k];
  psd(k,l,r);
  int mid=(l+r)>>1,res=0;
  if(x<=mid) res+=query(k<<1,l,mid,x,y);
  if(y>mid) res+=query(k<<1|1,mid+1,r,x,y);
  return res;
}
bool judge(int mid){
  x=mid;
  build(1,1,n);
  for(int i=1;i<=m;i++){
    int num=query(1,1,n,op[i].l,op[i].r);
    if(!op[i].ty){
      modify(1,1,n,op[i].l,op[i].r-num,0);
      modify(1,1,n,op[i].r-num+1,op[i].r,1);
    }
    else{
      modify(1,1,n,op[i].l,op[i].l+num-1,1);
      modify(1,1,n,op[i].l+num,op[i].r,0);
    }
  }
  return query(1,1,n,q,q);
}
void run(){
  int l=1,r=n;
  while(l<=r){
    int mid=(l+r)>>1;
    if(judge(mid)){ ans=mid; l=mid+1; }
    else r=mid-1;
  }
}
int main(){
#ifndef ONLINE_JUDGE
  freopen("x.in","r",stdin);
  freopen("x.out","w",stdout);
#endif
  init();
  run();
  printf("%d\n",ans);
}

[HEOI2016/TJOI2016]排序 解题报告

标签:另一个   一个   inline   要求   main   build   freopen   线段   代码   

原文地址:https://www.cnblogs.com/brucew-07/p/12193650.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!