「HEOI2016/TJOI2016」排序
「HEOI2016/TJOI2016」排序
题目大意
给定一个 \(1\) 到 \(n\) 的排列,每次可以对这个序列的一个区间进行升序/降序排序,求所有操作后第 \(q\) 个位置上的数字。
题解
大棒子,又学到了许多。
做法很多,这里大概讲一下主流的几种做法。
在线做法
- 线段树合并&分裂
其实将一个区间升序或降序排序可以看作同一个操作——进行升序排序,打一个是否是升序排序的标记。
所以我们可以在每一个位置维护一棵权值线段树,当要将区间 \([l,r]\) 的数字排序时,取出这些位置所维护的权值线段树合并至某一特定位置即可。
但是我们不可能每次合并过后又暴力将其分裂回每个位置上,这样复杂度肯定是吃不消的。所以我们考虑每一次在上一次的基础上取出我们想要的区间进行合并。
我们假设合并时将线段树合并至区间的左端点。
我们可以维护一个 \(\texttt{set}\),代表现存的线段树左端点标号。
我们在 \(\texttt{set}\) 中去二分找到包含端点的那棵权值线段树,然后根据升序/降序的标记做一个分类讨论。
设这个端点为 \(k\)。
升序:将这棵权值线段树分裂成 \([l,k-1]\) 和 \([k,r]\) 两个部分,则这个端点所维护的线段树区间为 \([k,r]\)。
降序:将这棵权值线段树分裂成 \([l,r-k-1]\) 和 \([r-k,r]\) 两个部分,则这个端点所维护的线段树区间为 \([l,r-k-1]\)。这里一定要注意因为是降序所以处理有一定不同。
记得要分裂出来的两棵线段树标记须与原来的那棵线段树保持一致。
时间复杂度为 \(O((n+m)\log_2n)\)
代码真的很可读,就没啥注释了OWO。
/*---Author:HenryHuang---*/
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int son[maxn*30][2],rs[maxn*30],bac[maxn*30],sum[maxn*30],cnt,tot;
int rt[maxn],tag[maxn];
int newnode(){
return cnt?bac[cnt--]:++tot;
}
void del(int u){
bac[++cnt]=u,son[u][0]=son[u][1]=sum[u]=0;
}
void update(int &u,int l,int r,int pos,int val){
if(!u) u=newnode();sum[u]+=val;
if(l==r) return ;int mid=(l+r)>>1;
if(pos<=mid) update(son[u][0],l,mid,pos,val);
else update(son[u][1],mid+1,r,pos,val);
return ;
}
void split(int x,int &y,int k){
y=newnode();
int v=sum[son[x][0]];
if(k>v) split(son[x][1],son[y][1],k-v);
else swap(son[x][1],son[y][1]);
if(k<v) split(son[x][0],son[y][0],k);
sum[y]=sum[x]-k,sum[x]=k;
return ;
}
int merge(int x,int y){
if(!x||!y) return x+y;
sum[x]+=sum[y];
son[x][0]=merge(son[x][0],son[y][0]);
son[x][1]=merge(son[x][1],son[y][1]);
del(y);
return x;
}
set<int> s;
set<int> ::iterator id(int x){
set<int> ::iterator it=s.lower_bound(x);
if(*it==x) return it;
int p=*it;//这就是r+1
int l=*--it;//这是l
if(!tag[l]) split(rt[l],rt[x],x-l),tag[x]=tag[l]=0;
else{
split(rt[l],rt[x],p-x),swap(rt[l],rt[x]),tag[l]=tag[x]=1;
}
return s.insert(x).first;
}
void solve(int l,int r){
set<int> ::iterator L=id(l),R=id(r+1);
for(set<int> ::iterator it=++L;it!=R;++it) rt[l]=merge(rt[l],rt[*it]);//合并
s.erase(L,R);//全部删掉,因为都合并掉了
}
int query(int u,int l,int r){
if(l==r) return l;
int mid=(l+r)>>1;
if(son[u][0]) return query(son[u][0],l,mid);
else return query(son[u][1],mid+1,r);
}//最后查答案
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int n,m;cin>>n>>m;
s.insert(n+1);
for(int i=1;i<=n;++i){
int x;cin>>x;
update(rt[i],1,n,x,1);
s.insert(i);
}
for(int i=1;i<=m;++i){
int opt,l,r;
cin>>opt>>l>>r;
solve(l,r);tag[l]=opt;
}
int q;cin>>q;
id(q),id(q+1);
cout<<query(rt[q],1,n)<<'\n';
return 0;
}
- 平衡树启发式合并&分裂
同理,只是将线段树换为 Splay/FHQ Treap。个人推荐FHQ Treap。(因为你不用额外徒增代码量,似乎复杂度也更对)
离线做法
- 线段树+二分
如果对于一个排列,我们进行区间排序显然是不好维护的。
那么对于一个 \(\texttt{0/1}\) 序列呢?
我们只需要进行统计 \(0,1\) 的个数,然后进行区间赋值即可。
所以我们的问题就变成了区间赋值,区间求和。
这个用一棵线段树可以直接维护。
所以我们考虑二分答案 \(x\),将 \(\ge x\) 的数设为 \(1\),其他数设为 \(0\)。
我们所需要的答案就是最后答案位置上的数字为 \(1\) 时的最大的 \(x\)。
最后的时间复杂度为 \(O(m\log_2^2n)\)。
「HEOI2016/TJOI2016」排序的更多相关文章
- 「HEOI2016/TJOI2016」 排序
题目链接 戳我 \(Solution\) 这道题在线的做法不会,所以这里就只讲离线的做法. 因为直接排序的话复杂度显然不对.但是如果数列为\(01\)串的话就可以让复杂度变成对的了 那么\(01\)串 ...
- 「HEOI2016/TJOI2016」序列
题目链接 戳这 Solution 首先考虑最暴力的dp 我们设: \(f[i]\)表示选择\(i\)以后所能形成的满足条件的子序列的最大值 \(minx[i]\)表示\(i\)能转换为的最小值 \(m ...
- loj #2055. 「TJOI / HEOI2016」排序
#2055. 「TJOI / HEOI2016」排序 题目描述 在 2016 年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他. 这个 ...
- 洛谷 P2824 [HEOI2016/TJOI2016]排序 解题报告
P2824 [HEOI2016/TJOI2016]排序 题意: 有一个长度为\(n\)的1-n的排列\(m\)次操作 \((0,l,r)\)表示序列从\(l\)到\(r\)降序 \((1,l,r)\) ...
- LibreOJ2241 - 「CQOI2014」排序机械臂
Portal Description 给出一个\(n(n\leq10^5)\)个数的序列\(\{a_n\}\),对该序列进行\(n\)次操作.若在第\(i\)次操作前第\(i\)小的数在\(p_i\) ...
- [HEOI2016/TJOI2016]排序 线段树+二分
[HEOI2016/TJOI2016]排序 内存限制:256 MiB 时间限制:6000 ms 标准输入输出 题目类型:传统 评测方式:文本比较 题目描述 在2016年,佳媛姐姐喜欢上了数字序列.因而 ...
- [Luogu P2824] [HEOI2016/TJOI2016]排序 (线段树+二分答案)
题面 传送门:https://www.luogu.org/problemnew/show/P2824 Solution 这题极其巧妙. 首先,如果直接做m次排序,显然会T得起飞. 注意一点:我们只需要 ...
- 2021.12.09 [HEOI2016/TJOI2016]排序(线段树+二分,把一个序列转换为01串)
2021.12.09 [HEOI2016/TJOI2016]排序(线段树+二分,把一个序列转换为01串) https://www.luogu.com.cn/problem/P2824 题意: 在 20 ...
- fir.im Weekly - 如何打造 Github 「爆款」开源项目
最近 Android 转用 Swift 的传闻甚嚣尘上,Swift 的 Github 主页上已经有了一次 merge>>「Port to Android」,让我们对 Swift 的想象又多 ...
随机推荐
- iOS视频硬编码技术
iOS视频硬编码技术 一.iOS视频采集硬编码 基本原理 硬编码 & 软编码 硬编码:通过系统自带的Camera录制视频,实际上调用的是底层的高清编码硬件模块,即显卡,不使用CPU,速度快 软 ...
- H.265视频编码与技术全析(上)
H.265视频编码与技术全析(上) 一. 概述 作为新一代视频编解码格式,H.265得到越来越广泛的应用.不久之前,苹果公司在翘首期盼中发布了iPhone6,该款手机较之以往的iPhone,不仅仅只是 ...
- GPU编程和流式多处理器
GPU编程和流式多处理器 流式多处理器(SM)是运行CUDA内核的GPU的一部分.本章重点介绍SM的指令集功能. 流式多处理器(SM)是运行我们的CUDA内核的GPU的一部分.每个SM包含以下内容. ...
- Python API vs C++ API of TensorRT
Python API vs C++ API of TensorRT 本质上,C++ API和Python API应该在支持您的需求方面接近相同.pythonapi的主要优点是数据预处理和后处理都很容易 ...
- Fine-Tuning微调原理
Fine-Tuning微调原理 如何在只有60000张图片的Fashion-MNIST训练数据集中训练模型.ImageNet,这是学术界使用最广泛的大型图像数据集,它拥有1000多万幅图像和1000多 ...
- 【读书笔记】《基于UG NX系统的二次开发》笔记
我有几本二次开发的书,但是从头到尾读下来的却没有几本.有时候遇到困难发帖求助,好不容易得到答案.后来却发现在书上的前几章就有详细介绍.读书笔记不仅是一种记录,更是一种督促自己读书的方法.还有一个原因是 ...
- 「模拟8.19 A嚎叫..(set) B主仆..(DFS) C征程..(DP+堆优化)」
为啥这一套题目背景感到很熟悉. T1 嚎叫响彻在贪婪的厂房 考试一个小时没调出来,自闭了.......... 正解很好想,最后实在打不出来了只好暴力骗分了... 联想到以前做的题:序列(涉及质因数分 ...
- 管理后台Vue
管理后台 遇到的问题 搭建 基于vue 3.0 Vue CLI 4.x Ant Design Vue 2.0 搭建后台管理系统 Ant Design Vue 2.0 npm i --save ant- ...
- GitHub Desktop的使用,创建项目、上传文件,设置忽略文件
下载登陆之后 新建项目File--第一个New repository 然后输入项目名称,选择项目文件夹,最后点Creata repository创建项目 这只是在本地建了项目. 项目文件夹中有其他文件 ...
- 浅析uniapp
前端跨平台框架 之uniapp入门浅析 技术的发展总日新月异,处在风口,前端技术的发展尤为迅速,跨平台的概念也在前端流行起来.从最早期PhoneGap.lonic.Cordova,到近年来的Reac ...