[bzoj4552]排序

解题关键:观察发现答案可进行二分,二分答案,将大于等于答案的数记为1,小于的记为0,从而可以使用线段树的区间赋值和区间求和解决。

复杂度:$O(nlog^2n)$

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<cstdlib>
  5 #include<cmath>
  6 #include<iostream>
  7 using namespace std;
  8 typedef long long ll;
  9 const int maxn=200005;
 10 int a[maxn],sum[maxn<<3],add[maxn<<3];
 11 int t1[maxn],t2[maxn],t3[maxn],v[maxn];
 12 int n,m,r;
 13
 14 void pushdown(int rt,int m){
 15     if(add[rt]!=-1){
 16         add[rt<<1]=add[rt];
 17         add[rt<<1|1]=add[rt];
 18         sum[rt<<1]=add[rt]*(m-(m>>1));
 19         sum[rt<<1|1]=add[rt]*(m>>1);
 20         add[rt]=-1;
 21     }
 22 }
 23
 24 void pushup(int rt){
 25     sum[rt]=sum[rt<<1]+sum[rt<<1|1];
 26 }
 27
 28 void build(int rt,int l,int r){
 29     add[rt]=-1;
 30     if(l==r){
 31         sum[rt]=v[l];
 32         return;
 33     }
 34     int mid=(l+r)>>1;
 35     build(rt<<1,l,mid);
 36     build(rt<<1|1, mid+1, r);
 37     pushup(rt);
 38 }
 39
 40 void update(int rt,int l,int r,int tl,int tr,int c){
 41     if(tl>tr) return;//落掉这句话导致在bzoj一直re
 42     if(tl<=l&&tr>=r){
 43         sum[rt]=c*(r-l+1);
 44         add[rt]=c;
 45         return;
 46     }
 47     pushdown(rt, r-l+1);
 48     int mid=(l+r)>>1;
 49     if(tl<=mid) update(rt<<1,l,mid,tl,tr, c);
 50     if(tr>mid)  update(rt<<1|1, mid+1, r, tl, tr, c);
 51     pushup(rt);
 52 }
 53
 54 int query(int rt,int l,int r,int tl,int tr){
 55     if(tl<=l&&tr>=r){
 56         return sum[rt];
 57     }
 58     pushdown(rt,r-l+1);
 59     int mid=(l+r)>>1,res=0;
 60     if(tl<=mid) res+=query(rt<<1,l,mid,tl,tr);
 61     if(tr>mid) res+=query(rt<<1|1,mid+1,r,tl,tr);
 62     return res;
 63 }
 64
 65 bool check(int x){
 66     for(int i=1;i<=n;i++) v[i]= a[i]>=x?1:0;
 67     build(1, 1, n);
 68     for(int i=0;i<m;i++){
 69         if(t1[i]){
 70             int tmp=query(1, 1, n, t2[i], t3[i]);
 71             update(1, 1, n, t2[i], t2[i]+tmp-1, 1);
 72             update(1, 1, n, t2[i]+tmp, t3[i], 0);
 73         }
 74         else{
 75             int tmp=query(1, 1, n, t2[i], t3[i]);
 76             update(1, 1, n, t2[i], t3[i]-tmp, 0);
 77             update(1, 1, n, t3[i]-tmp+1, t3[i], 1);
 78         }
 79     }
 80     if(query(1, 1, n, r, r)==1) return true;
 81     else return false;
 82 }
 83
 84 int erfen(int l,int r){
 85     while(l<r){
 86         int mid=(l+r+1)>>1;
 87         if(check(mid)) l=mid;
 88         else r=mid-1;
 89     }
 90     return r;
 91 }
 92
 93 int main(){
 94     scanf("%d%d",&n,&m);
 95     for(int i=1;i<=n;i++)   scanf("%d",a+i);
 96     for(int i=0;i<m;i++)    scanf("%d%d%d",t1+i,t2+i,t3+i);
 97     scanf("%d",&r);
 98     int ans=erfen(1, n);
 99     printf("%d\n",ans);
100     return 0;
101 }
时间: 2024-10-22 22:17:43

[bzoj4552]排序的相关文章

bzoj4552排序(线段树,二分)

题目大意 给定一个长度为n的序列,有m个操作,操作包括两种: \(0\ l\ r\)区间[l,r]的数字升序排序 \(1\ l\ r\)区间[l,r]的数字降序排序 最后询问在q位置上的数是多少? 其中\(n \le 100000,m\le 100000\) QWQ这个题是看了题解才会的,感觉思路很不错 我们考虑,这个题的询问其实只有一组,所以我们可以 二分一个最终在q的数是多少(或者说在原来的排名是多少) 每次将大于等于\(mid\)的数变为1,小于的为0. 那么对于升序排序,假设这个区间有\

【BZOJ4552】排序(线段树,二分答案)

[BZOJ4552]排序(线段树,二分答案) 题面 BZOJ 题解 好神的题啊 直接排序我们做不到 怎么维护? 考虑一下,如果我们随便假设一个答案 怎么检验它是否成立? 把这个数设成\(1\),其他的数字都设成\(0\) 最后检查一下这个位置是不是\(1\)就好啦 但是这样没法排序 那么,我们考虑二分一个答案, 把所有比\(mid\)大的数都设成\(1\) 这样,如果在第\(Q\)位上的数字是\(1\) 意味着有一个不小于当前\(mid\)的数在这个位置上 否则就是一个比\(mid\)小的数在这

bzoj4552 [Tjoi2016&amp;Heoi2016]排序

Description 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排序分为两种:1:(0,l,r)表示将区间[l,r]的数字升序排序2:(1,l,r)表示将区间[l,r]的数字降序排序最后询问第q位置上的数字. Input 输入数据的第一行为两个整数n和m.n表示序列的长度,m表示局部排序的次数.1 <= n, m <= 10^5第二

[bzoj4552][Tjoi2016][Heoi2016]排序

Description 给出一个到的全排列,现在对这个全排列序列进行次局部排序,排序分为种: 表示将区间的数字升序排序; 表示将区间的数字降序排序. 最后询问第位置上的数字. Input 第行为两个整数和.表示序列的长度,表示局部排序的次数. 第行为个整数,表示到的一个全排列. 接下来输入行,每行有个整数 为代表升序排序,为1代表降序排序;表示排序的区间. 最后输入一个整数,表示排序完之后询问的位置. Output 输出数据仅有一行一个整数,表示按照顺序将全部的部分排序结束后第位置上的数字. S

bzoj4552【TJOI2016&amp;HEOI2016】排序

4552: [Tjoi2016&Heoi2016]排序 Time Limit: 60 Sec  Memory Limit: 256 MB Submit: 291  Solved: 171 [Submit][Status][Discuss] Description 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题 ,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排 序分为两种:1:(0,l,

[bzoj4552][TJOI&amp;HEOI2016]排序

题目大意 有一个n的排列,进行m次操作,每次操作是将一个区间升序或降序排序. 请你输出m次操作后第p个位置的值. 二分答案 题解好机智! 我们二分答案x,然后就是判断a[p]>=x? 把原序列转化为01序列,0表示小于x,1表示大于等于x. 那么区间升序排序其实就是把0全放前面,1都放后面. 用线段树兹瓷区间赋值就好了. 然后只需要维护区间0的个数. #include<cstdio> #include<algorithm> #define fo(i,a,b) for(i=a;

【BZOJ4552】【HEOI2016】排序 [二分答案][线段树]

排序 Time Limit: 60 Sec  Memory Limit: 256 MB[Submit][Status][Discuss] Description 在2016年,佳媛姐姐喜欢上了数字序列. 因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他. 这个难题是这样子的: 给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排序分为两种: 1: (0,l,r)表示将区间[l,r]的数字升序排序 2: (1,l,r)表示将区间[l,r]的数字降序排序

【bzoj4552】【Tjoi2016&amp;Heoi2016】【NOIP2016模拟7.12】排序

题目 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排序分为两种:1:(0,l,r)表示将区间[l,r]的数字升序排序2:(1,l,r)表示将区间[l,r]的数字降序排序最后询问第q 位置上的数字. 分析 二分答案, 把二分出的ans与原序列比较,小于ans的数改为-1,大于ans的数改为1. 对于输入的每一个修改,用线段树来处理. 最后求

【bzoj4552】排序

二分一个值,然后线段树上模拟. #include<bits/stdc++.h> #define lson (o<<1) #define rson (o<<1|1) const int N=1e5+10; using namespace std; int n,m,a[N],x,q; struct Opt{int opt,l,r,id;}b[N]; struct Segment_Tree{ int sumv[N<<2],setv[N<<2]; inl