解法一:二分答案+线段树

首先我们知道,对于一个01序列排序,用线段树维护的话可以做到单次排序复杂度仅为log级别。

这道题只有一个询问,所以离线没有意义,而一个询问让我们很自然的想到二分答案。先二分出这个位置上的数是多少,然后将所有小于等于的数全部赋为0,其余赋为1,这样每次排序都是01序列排序了。如果最后p位置上的数为0则说明最终答案小于等于当前二分的答案,反之亦然。

这样这个问题就在$O(n \log^2 n)$的复杂度内解决了。

 #include<cstdio>
#include<algorithm>
#define ls (x<<1)
#define rs ((x<<1)|1)
#define lson ls,L,mid
#define rson rs,mid+1,R
#define rep(i,l,r) for (int i=l; i<=r; i++)
using namespace std; const int N=;
int n,m,qry,a[N],c[N],sm[N<<],tag[N<<];
struct P{ int op,l,r; }b[N]; void push(int x,int L,int R){
if (tag[x]==-) return;
int mid=(L+R)>>;
sm[ls]=(mid-L+)*tag[x]; tag[ls]=tag[x];
sm[rs]=(R-mid)*tag[x]; tag[rs]=tag[x];
tag[x]=-;
} void build(int x,int L,int R){
tag[x]=-;
if (L==R) { sm[x]=c[L]; return; }
int mid=(L+R)>>;
build(lson); build(rson);
sm[x]=sm[ls]+sm[rs];
} void mdf(int x,int L,int R,int l,int r,int k){
if (L==l && r==R){ sm[x]=k*(R-L+); tag[x]=k; return; }
int mid=(L+R)>>; push(x,L,R);
if (r<=mid) mdf(lson,l,r,k);
else if (l>mid) mdf(rson,l,r,k);
else mdf(lson,l,mid,k),mdf(rson,mid+,r,k);
sm[x]=sm[ls]+sm[rs];
} int que(int x,int L,int R,int l,int r){
if (L==l && r==R) return sm[x];
int mid=(L+R)>>; push(x,L,R);
if (r<=mid) return que(lson,l,r);
else if (l>mid) return que(rson,l,r);
else return que(lson,l,mid)+que(rson,mid+,r);
} int main(){
scanf("%d%d",&n,&m);
rep(i,,n) scanf("%d",&a[i]);
rep(i,,m) scanf("%d%d%d",&b[i].op,&b[i].l,&b[i].r);
scanf("%d",&qry);
int L=,R=n;
while (L<R){
int mid=(L+R)>>;
rep(i,,n) if (a[i]<=mid) c[i]=; else c[i]=;
build(,,n);
rep(i,,m){
int l=b[i].l,r=b[i].r,s=que(,,n,l,r);
if (b[i].op==){
if (l<=r-s) mdf(,,n,l,r-s,);
if (r-s+<=r) mdf(,,n,r-s+,r,);
}else{
if (l<=l+s-) mdf(,,n,l,l+s-,);
if (l+s<=r) mdf(,,n,l+s,r,);
}
}
if (que(,,n,qry,qry)==) R=mid; else L=mid+;
}
printf("%d\n",L);
return ;
}

解法二:线段树分裂与合并

对每个连续的有序区间维护一棵线段树(初始时每个点都有一棵线段树)。

每次将一个区间排序时,先将跨过这个区间端点的区间分裂,再将这个区间包含的所有小区间合并。

set维护区间,区间合并通过线段树合并实现,时间复杂度为$O(n\log n)$。

可以垃圾回收,由于每次分裂只会新增log n个点,加上初始时新建的点,空间复杂度为$O((n+m)\log n)$。

这个方法不仅复杂度只有一个log,还能在最后求出整个序列而不是单个序列。

 #include<set>
#include<cstdio>
#include<algorithm>
#define lson ls[x],L,mid
#define rson rs[x],mid+1,R
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
using namespace std; const int N=,M=;
int n,m,x,nd,op,l,r,top,rt[N],rev[N],stk[M],ls[M],rs[M],sz[M];
struct P{ int l,r; };
bool operator <(const P &a,const P &b){ return a.r<b.r; }
set<P>S; void del(int x){ ls[x]=rs[x]=sz[x]=; stk[++top]=x; }
int get(){ return top ? stk[top--] : ++nd; } void ins(int &x,int L,int R,int k){
if (!x) x=get(); sz[x]++;
if (L==R) return;
int mid=(L+R)>>;
if (k<=mid) ins(lson,k); else ins(rson,k);
} void split(int &x,int &y,int k,int op){
if (!x) return;
sz[y=get()]=sz[x]-k; sz[x]=k;
if (!op){
if (sz[ls[x]]==k){ rs[y]=rs[x]; rs[x]=; return; }
if (sz[ls[x]]>k){ rs[y]=rs[x]; rs[x]=; split(ls[x],ls[y],k,op); }
else split(rs[x],rs[y],k-sz[ls[x]],op);
}else{
if (sz[rs[x]]==k){ ls[y]=ls[x]; ls[x]=; return; }
if (sz[rs[x]]>k){ ls[y]=ls[x]; ls[x]=; split(rs[x],rs[y],k,op); }
else split(ls[x],ls[y],k-sz[rs[x]],op);
}
} int merge(int x,int y){
if (!x || !y) return x|y;
sz[x]+=sz[y];
ls[x]=merge(ls[x],ls[y]);
rs[x]=merge(rs[x],rs[y]);
del(y); return x;
} int que(int x,int L,int R,int k,int op){
if (L==R) return L;
int mid=(L+R)>>;
if (!op){
if (sz[ls[x]]>=k) return que(lson,k,op); else return que(rson,k-sz[ls[x]],op);
}else{
if (sz[rs[x]]>=k) return que(rson,k,op); else return que(lson,k-sz[rs[x]],op);
}
} int main(){
freopen("bzoj4552.in","r",stdin);
freopen("bzoj4552.out","w",stdout);
scanf("%d%d",&n,&m);
rep(i,,n) scanf("%d",&x),ins(rt[i],,n,x),S.insert((P){i,i});
while (m--){
scanf("%d%d%d",&op,&l,&r);
set<P>::iterator it=S.lower_bound((P){l,l});
if (it->l<l){
int L=it->l,R=it->r;
split(rt[L],rt[l],l-L,rev[L]);
S.erase(it); S.insert((P){L,l-}); S.insert((P){l,R});
rev[l]=rev[L];
}
it=S.lower_bound((P){r+,r+});
if (it!=S.end() && it->l<=r){
int L=it->l,R=it->r;
split(rt[L],rt[r+],r-L+,rev[L]);
S.erase(it); S.insert((P){L,r}); S.insert((P){r+,R});
rev[r+]=rev[L];
}
it=S.lower_bound((P){l,l}); it++;
while (it!=S.end() && it->r<=r) rt[l]=merge(rt[l],rt[it->l]),it++;
it=S.lower_bound((P){l,l});
while (it!=S.end() && it->r<=r) S.erase(it),it=S.lower_bound((P){l,l});
S.insert((P){l,r}); rev[l]=op;
}
scanf("%d",&x);
set<P>::iterator it=S.lower_bound((P){x,x});
printf("%d\n",que(rt[it->l],,n,x-(it->l)+,rev[it->l]));
return ;
}

[BZOJ4552][TJOI2016&&HEOI2016]排序(二分答案+线段树/线段树分裂与合并)的更多相关文章

  1. BZOJ 4552 [Tjoi2016&Heoi2016]排序 | 二分答案 线段树

    题目链接 题面 题目描述 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这 ...

  2. bzoj 4552 [Tjoi2016&Heoi2016]排序——二分答案

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4552 二分答案,把 >= mid 的设成1.< mid 的设成0,之后排序就变成 ...

  3. bzoj 4552 [Tjoi2016&Heoi2016]排序 (二分答案 线段树)

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4552 题意: 给你一个1-n的全排列,m次操作,操作由两种:1.将[l,r]升序排序,2 ...

  4. [bzoj4552][Tjoi2016&Heoi2016]排序-二分+线段树

    Brief Description DZY有一个数列a[1..n],它是1∼n这n个正整数的一个排列. 现在他想支持两种操作: 0, l, r: 将a[l..r]原地升序排序. 1, l, r: 将a ...

  5. 【BZOJ4552】[Tjoi2016&Heoi2016]排序 二分+线段树

    [BZOJ4552][Tjoi2016&Heoi2016]排序 Description 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题 ...

  6. bzoj千题计划128:bzoj4552: [Tjoi2016&Heoi2016]排序

    http://www.lydsy.com/JudgeOnline/problem.php?id=4552 二分答案 把>=mid 的数看做1,<mid 的数看做0 这样升序.降序排列相当于 ...

  7. [BZOJ4552][Tjoi2016&Heoi2016]排序(二分答案+线段树)

    二分答案mid,将>=mid的设为1,<mid的设为0,这样排序就变成了区间修改的操作,维护一下区间和即可 然后询问第q个位置的值,为1说明>=mid,以上 时间复杂度O(nlog2 ...

  8. 2018.08.01 BZOJ4552: [Tjoi2016&Heoi2016]排序(二分+线段树)

    传送门 线段树简单题. 二分答案+线段树排序. 实际上就是二分答案mid" role="presentation" style="position: relat ...

  9. [BZOJ4556][TJOI2016&&HEOI2016]字符串(二分答案+后缀数组+RMQ+主席树)

    4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1360  Solved: 545[S ...

随机推荐

  1. 【洛谷 P4166】 [SCOI2007]最大土地面积(凸包,旋转卡壳)

    题目链接 又调了我两个多小时巨亏 直接\(O(n^4)\)枚举4个点显然不行. 数据范围提示我们需要一个\(O(n^2)\)的算法. 于是\(O(n^2)\)枚举对角线,然后在这两个点两边各找一个点使 ...

  2. hihoCoder 1174 : 拓扑排序·一

    题目链接:http://hihocoder.com/problemset/problem/1174 题目是中文题面我就不说题意了,要看题面的请点击上方链接~ 代码实现如下: #include < ...

  3. React Native 与 夜神模拟器的绑定

    之前一直用真机去调试, 每回更新一次都需要手动摇晃手机后才能reload JS, OMG,太麻烦了. 后来寻思模拟器网上推荐用Geny...什么的模拟器,但是那个模拟器还需要VBox一起用. 有点麻烦 ...

  4. Lucene7.2.1系列(三)查询及高亮

    系列文章: Lucene系列(一)快速入门 Lucene系列(二)luke使用及索引文档的基本操作 Lucene系列(三)查询及高亮 一 准备 创建项目并添加Maven依赖 <dependenc ...

  5. 在64位ubuntu中安装代码比较工具beyond compare

    1,//从http://www.scootersoftware.com/download.php 官方地址下载 bcompare-3.3.2.14050.tar.gz 或 bcompare-4.0.7 ...

  6. 一个python爬虫工具类

    写了一个爬虫工具类. # -*- coding: utf-8 -*- # @Time : 2018/8/7 16:29 # @Author : cxa # @File : utils.py # @So ...

  7. vue头像上传

    项目四知识点 默认头像 选择头像 <template> <div class="adatar"> <img :src="adatar?ada ...

  8. php+mysql缓存技术的实现

    本教程适合于那些对缓存SQL查询以减少数据库连接与执行的负载.提高脚本性能感兴趣的PHP程序员.概述 许多站点使用数据库作为站点数据存储的容器.数据库包含了产器信息.目录结构.文章或者留言本,有些数据 ...

  9. C#串口serialPort操作

    现在大多数硬件设备均采用串口技术与计算机相连,因此串口的应用程序开发越来越普遍.例如,在计算机没有安装网卡的情况下,将本机上的一些信息数据 传输到另一台计算机上,那么利用串口通信就可以实现.运行本程序 ...

  10. php 高并发解决方案(用于抢购) 转载

    最近在做一个团购项目,遇到个问题,就是在抢购.秒杀.抽奖等活动时,库存数量有限,但是同时下单人数超过了库存数量,就会导致商品超售问题.那么我们怎么来解决这个问题呢,我的思路如下: sql1:查询商品库 ...