题目链接:

https://www.luogu.org/problemnew/show/P2824

题目描述:

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

题解:

做法一:二分答案

然而我并没有写,口胡一下就是二分最终的那个数字,假设我们二分的值是$val$,那么在序列中大于等于$val$我们看成1,小于$val$的我们看成0,这样就是一个01序列了。每次操作我们就线段树维护0的个数和1的个数然后区间赋值就是了。最终我们看一下第q个位置是0还是1,如果是1就把$val$调大,否则调小。

做法二:线段树分裂合并+set

主要讲讲这个吧

考虑我们一开始对每个元素建一颗动态开点权值线段树,因为是1到n的排列我们都不需要离散化(其实本身是动态开点好像也没有很大的影响)。这样我们就得到了一个个由一个元素构成的区间,在之后的操作中然后我们用set维护一下每个区间的左端点,右端点,这个区间线段树的根节点和这个区间是单调增的还是单调减的。

不管是操作1还是操作2,我们就是把[l,r]的所有元素合并到一棵线段树,唯一麻烦的就是端点l,r可能是在当前的某个区间里面,这样的话我们还需要把这个区间给分裂,再把我们需要的那部分拿出来合并

代码实现是这样的,注意我们还要修改一下set

set<Dat>::iterator it=s.lower_bound((Dat){,l,,});
if ((*it).l!=l)
{
Dat t=*it;s.erase(it);int tn=;
if (t.type==)
{
Seg::split(,n,t.nd,tn,l-t.l);
s.insert((Dat){t.l,l-,tn,});
s.insert((Dat){l,t.r,t.nd,});
//printf("qq%d %d\n",t.l,l-1);
//printf("qq%d %d\n",l,t.r);
}
else
{
Seg::split(,n,t.nd,tn,t.r-l+);
s.insert((Dat){t.l,l-,t.nd,});
s.insert((Dat){l,t.r,tn,});
}
}
it=s.lower_bound((Dat){,r,,});
if ((*it).r!=r)
{
Dat t=*it;s.erase(it);int tn=;
if (t.type==)
{
Seg::split(,n,t.nd,tn,r-t.l+);
//printf("qq%d\n",tn);
s.insert((Dat){t.l,r,tn,});
s.insert((Dat){r+,t.r,t.nd,});
}
else
{
Seg::split(,n,t.nd,tn,t.r-r);
s.insert((Dat){t.l,r,t.nd,});
s.insert((Dat){r+,t.r,tn,});
}
}

it就是我们当前要分裂的区间,(*it).nd就是这个区间的根节点,l,r是我们这次操作的区间。对于Dat这个结构体我们采取右端点优先,左端点次一级的排序。

由于升降序不同,我们要从要分裂的区间的线段树取出来的部分也不同,可能是后面的,也可能是前面的,这就需要分类讨论一下了(不要怕麻烦,懒得话就不要学这种做法)

还学到了一个回收空间内存的做法,具体时间就是用一个栈保存一下可以使用的结点,每次要用结点的时候让一个元素出栈即可,可见getnode函数

#include<algorithm>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<set>
using namespace std; int n,m;
namespace Seg
{
const int N=6e5+;
int lx[N],rx[N],siz[N];
int st[N+];int top;
void init() {for (int i=N-;i>=;i--) st[++top]=i;}
int getnode() {int t=st[top--];lx[t]=rx[t]=siz[t]=;return t;}
void delnode(int x) {st[++top]=x;}
#define mid ((l+r)>>1)
void update(int &o,int l,int r,int x)
{
if (!o) o=getnode();
if (l==r) {siz[o]++;return;}
if (x<=mid) update(lx[o],l,mid,x);
else update(rx[o],mid+,r,x);
siz[o]=siz[lx[o]]+siz[rx[o]];
}
void split(int l,int r,int o,int &x,int k)
{
if (!k) return;
if (!x) x=getnode();
if (l==r) {siz[o]-=k;siz[x]+=k;return;}
int t=siz[lx[o]];
if (t>k) split(l,mid,lx[o],lx[x],k);
if (t==k) lx[x]=lx[o],lx[o]=;
if (t<k)
{
lx[x]=lx[o];lx[o]=;
split(mid+,r,rx[o],rx[x],k-t);
}
siz[o]=siz[lx[o]]+siz[rx[o]];
siz[x]=siz[lx[x]]+siz[rx[x]];
}
int merge(int x,int y)
{
if (!x||!y) return x|y;
lx[x]=merge(lx[x],lx[y]);
rx[x]=merge(rx[x],rx[y]);
siz[x]+=siz[y];
delnode(y);
return x;
}
int kth(int l,int r,int o,int k)
{
if (l==r) return l;
if (siz[lx[o]]>=k) return kth(l,mid,lx[o],k);
else return kth(mid+,r,rx[o],k-siz[lx[o]]);
}
}
struct Dat
{
int l,r,nd,type;
};
bool operator <(const Dat &a,const Dat &b)
{
return a.r<b.r||(a.r==b.r&&a.l<b.l);
}
set <Dat> s;
inline int read()
{
char ch=getchar();
int s=,f=;
while (ch<''||ch>'') {if (ch=='-') f=-;ch=getchar();}
while (ch>=''&&ch<='') {s=(s<<)+(s<<)+ch-'';ch=getchar();}
return s*f;
}
int split_node(int l,int r)
{
set<Dat>::iterator it=s.lower_bound((Dat){,l,,});
if ((*it).l!=l)
{
Dat t=*it;s.erase(it);int tn=;
if (t.type==)
{
Seg::split(,n,t.nd,tn,l-t.l);
s.insert((Dat){t.l,l-,tn,});
s.insert((Dat){l,t.r,t.nd,});
//printf("qq%d %d\n",t.l,l-1);
//printf("qq%d %d\n",l,t.r);
}
else
{
Seg::split(,n,t.nd,tn,t.r-l+);
s.insert((Dat){t.l,l-,t.nd,});
s.insert((Dat){l,t.r,tn,});
}
}
it=s.lower_bound((Dat){,r,,});
if ((*it).r!=r)
{
Dat t=*it;s.erase(it);int tn=;
if (t.type==)
{
Seg::split(,n,t.nd,tn,r-t.l+);
//printf("qq%d\n",tn);
s.insert((Dat){t.l,r,tn,});
s.insert((Dat){r+,t.r,t.nd,});
}
else
{
Seg::split(,n,t.nd,tn,t.r-r);
s.insert((Dat){t.l,r,t.nd,});
s.insert((Dat){r+,t.r,tn,});
}
}
int re=;
while ()
{
it=s.lower_bound((Dat){,l,,});
if (it==s.end()||(*it).l>r) break;
Dat t=(*it);s.erase(it);
re=Seg::merge(re,t.nd);
}
//printf("%d\n",re);
return re;
}
int main()
{
Seg::init();
n=read();m=read();
for (int i=;i<=n;i++)
{
int t=;
Seg::update(t,,n,read());
//printf("%d\n",t);
s.insert((Dat){i,i,t,});
}
while (m--)
{
int op=read(),l=read(),r=read();
int t=split_node(l,r);
s.insert((Dat){l,r,t,op});
}
int q=read();
int t=split_node(q,q);
//printf("%d\n",t);
printf("%d\n",Seg::kth(,n,t,));
return ;
}

[HEOI2016/TJOI2016] 排序 解题报告(二分答案/线段树分裂合并+set)的更多相关文章

  1. 洛谷 P2824 [HEOI2016/TJOI2016]排序 解题报告

    P2824 [HEOI2016/TJOI2016]排序 题意: 有一个长度为\(n\)的1-n的排列\(m\)次操作 \((0,l,r)\)表示序列从\(l\)到\(r\)降序 \((1,l,r)\) ...

  2. [BZOJ4552][TJOI2016&&HEOI2016]排序(二分答案+线段树/线段树分裂与合并)

    解法一:二分答案+线段树 首先我们知道,对于一个01序列排序,用线段树维护的话可以做到单次排序复杂度仅为log级别. 这道题只有一个询问,所以离线没有意义,而一个询问让我们很自然的想到二分答案.先二分 ...

  3. Codeforces Round #276 (Div. 1) E. Sign on Fence (二分答案 主席树 区间合并)

    链接:http://codeforces.com/contest/484/problem/E 题意: 给你n个数的,每个数代表高度: 再给出m个询问,每次询问[l,r]区间内连续w个数的最大的最小值: ...

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

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

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

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

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

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

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

    排序 Time Limit: 60 Sec  Memory Limit: 256 MB[Submit][Status][Discuss] Description 在2016年,佳媛姐姐喜欢上了数字序列 ...

  8. 【Luogu】P2824排序(二分答案+线段树排序)

    题目链接 震惊!两个线段树和一个线段树竟是50分的差距! 本题可以使用二分答案,二分那个位置上最后是什么数.怎么验证呢? 把原序列改变,大于等于mid的全部变成1,小于mid的全部变成0,之后线段树排 ...

  9. BZOJ4556 [Tjoi2016&Heoi2016]字符串 SA ST表 二分答案 主席树

    原文链接https://www.cnblogs.com/zhouzhendong/p/BZOJ4556.html 题目传送门 - BZOJ4556 题意 给定一个长度为 $n$ 的字符串 $s$ . ...

随机推荐

  1. poj--3630--Phone List(字典树+前缀判断)

    Phone List Time Limit: 1000MS   Memory Limit: 65536KB   64bit IO Format: %I64d & %I64u Submit St ...

  2. 均匀分布(uniform distribution)期望的最大似然估计(maximum likelihood estimation)

    maximum estimator method more known as MLE of a uniform distribution [0,θ] 区间上的均匀分布为例,独立同分布地采样样本 x1, ...

  3. [SCOI 2009] 生日快乐

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=1024 [算法] 直接DFS,即可 [代码] #include<bits/std ...

  4. BZOJ 3052 树上带修莫队

    思路: 就是把带修莫队移到了树上 块的大小开到(n^2/3)/2 比较好- 这是一个卡OJ好题 //By SiriusRen #include <cmath> #include <c ...

  5. 【转自网络】JS实现保存当前网页HTML到本地

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  6. Centos7 minimal 系列之Nginx搭建(三)

    一.安装nginx 1.1.安装依赖包 yum -y install gcc-c++ yum -y install pcre pcre-devel yum -y install zlib zlib-d ...

  7. Python3之时间模块详述

    Python3之时间模块  time & datetime & calendar 一. 概述 python 提供很多方式处理日期与时间,转换日期格式是一个常见的功能. 时间元组:很多p ...

  8. D. Destruction of a Tree_dfs序_性质分析_思维题

    题意: 给定一棵树,每次可以拆掉一个树上度数为偶数的点,拆掉该点后,与该点所连的所有边都会被删掉.问,是否有一种删点顺序可以删掉所有的点.如果有,则输出任意一组解. 数据范围:线性做法 O(n)O(n ...

  9. jq——css类

    1  addClass(classname) 添加类 <script type="text/javascript"> $("input").clic ...

  10. UVA340-Master-Mind Hints(紫书例题3.4)

    MasterMind is a game for two players. One of them, Designer, selects a secret code. The other, Break ...