JZOJ4605. 排序(线段树合并与分裂)
题目大意:
每次把一个区间升序或降序排序,最后问一个点是什么。
题解:
如果只是问一个点,这确乎是个经典题,二分一下答案然后线段树维护01排序。
从pty那里get到了可以用线段树的合并与分裂实时地维护整个序列。
考虑一次排序就把这个区间的数搞到一个线段树上,在根处标记是正的还是反的。
如果想搞到一棵树上就需要用到分裂与合并,根据势能分析,复杂度还是\(O(n~log~n)\)。
Code:
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i < B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;
const int N = 1e5 + 5;
int n, m, a[N];
int op, l, r;
struct tree {
int l, r, x;
} t[N * 120];
#define i0 t[i].l
#define i1 t[i].r
int tot, pl, pr, px;
int rt[N * 120], rev[N * 120];
void upd(int i) {
t[i].x = t[i0].x + t[i1].x;
}
void add(int &i, int x, int y) {
i = ++ tot;
t[i].x = 1;
if(x == y) return;
int m = x + y >> 1;
if(pl <= m) add(i0, x, m); else add(i1, m + 1, y);
}
void merge(int &i, int j) {
if(!i || !j) { i = i + j; return;}
merge(t[i].l, t[j].l);
merge(t[i].r, t[j].r);
upd(i);
}
void ft(int i, int x, int y) {
if(x == y) { if(t[i].x == pl) px = x; return;}
int m = x + y >> 1;
if(t[i0].x >= pl) ft(i0, x, m); else
pl -= t[i0].x, ft(i1, m + 1, y);
}
void sp(int i, int &j1, int &j2, int x, int y, int k) {
if(!i) return;
if(y <= k) { j1 = i, j2 = 0; return;}
if(x > k) { j1 = 0, j2 = i; return;}
int m = x + y >> 1;
if(!j1) j1 = ++ tot;
if(!j2) j2 = ++ tot;
sp(i0, t[j1].l, t[j2].l, x, m, k);
sp(i1, t[j1].r, t[j2].r, m + 1, y, k);
upd(j1); upd(j2);
}
int bz[N];
void dg(int i, int x, int y) {
if(!t[i].x) return;
if(x == y) { bz[x] ++; return;}
int m = x + y >> 1;
dg(i0, x, m); dg(i1, m + 1, y);
}
void split(int &i, int &j, int k) {
pl = rev[i] ? t[i].x - k : k;
px = 0; ft(i, 0, n);
if(rev[i]) sp(i, j, i, 0, n, px); else sp(i, i, j, 0, n, px);
}
set<int> s;
set<int> :: iterator st, en;
int d[N], d0;
int main() {
scanf("%d %d", &n, &m);
fo(i, 1, n) scanf("%d", &a[i]);
fo(i, 0, n + 1) s.insert(i);
fo(i, 0, n) pl = pr = a[i], add(rt[i], 0, n);
fo(i, 1, m) {
scanf("%d %d %d", &op, &l, &r);
en = s.upper_bound(r);
st = -- s.lower_bound(l);
d0 = 0;
for(; st != en; st ++) d[++ d0] = *st;
d[++ d0] = *en;
int rt1 = 0, rt2 = 0;
split(rt[d[1]], rt1, l - d[1]);
if(t[rt1].x) {
rt[l] = rt1;
rev[rt[l]] = rev[rt[d[1]]];
d[1] = l;
}
if(d[d0] != r + 1) {
split(rt[d[d0 - 1]], rt2, r + 1 - d[d0 - 1]);
rt[r + 1] = rt2;
rev[rt[r + 1]] = rev[rt[d[d0 - 1]]];
}
fo(j, 2, d0 - 1) {
if(l != d[j]) merge(rt[l], rt[d[j]]), rt[d[j]] = 0;
s.erase(d[j]);
}
rev[rt[l]] = op;
s.insert(l); s.insert(r + 1);
}
scanf("%d", &l);
l ++;
for(st = s.begin(); st != s.end(); st ++) {
int i = *st;
if(t[rt[i]].x >= l) {
pl = rev[rt[i]] ? t[rt[i]].x - l + 1 : l;
px = 0;
ft(rt[i], 0, n);
pp("%d\n", px);
return 0;
} else l -= t[rt[i]].x;
}
}
JZOJ4605. 排序(线段树合并与分裂)的更多相关文章
- BZOJ.4552.[HEOI2016/TJOI2016]排序(线段树合并/二分 线段树)
题目链接 对于序列上每一段连续区间的数我们都可以动态开点建一棵值域线段树.初始时就是\(n\)棵. 对于每次操作,我们可以将\([l,r]\)的数分别从之前它所属的若干段区间中分离出来,合并. 对于升 ...
- BZOJ4552 HEOI2016/TJOI2016排序(线段树合并+线段树分裂)
很久以前写过二分答案离线的做法,比较好理解.事实上这还是一个线段树合并+分裂的板子题,相比离线做法以更优的复杂度做了更多的事情.具体不说了.怎么交了一遍luogu上就跑第一了啊 #include< ...
- 有趣的线段树模板合集(线段树,最短/长路,单调栈,线段树合并,线段树分裂,树上差分,Tarjan-LCA,势能线段树,李超线段树)
线段树分裂 以某个键值为中点将线段树分裂成左右两部分,应该类似Treap的分裂吧(我菜不会Treap).一般应用于区间排序. 方法很简单,就是把分裂之后的两棵树的重复的\(\log\)个节点新建出来, ...
- 【线段树合并】【P2824】 [HEOI2016/TJOI2016]排序
Description 给定一个长度为 \(n\) 的排列,有 \(m\) 次操作,每次选取一段局部进行升序或降序排序,问你一波操作后某个位置上的数字是几 Hint \(1~\leq~n,~m~\le ...
- 洛谷 P2824 [HEOI2016/TJOI2016]排序 (线段树合并)
(另外:题解中有一种思路很高妙而且看上去可以适用一些其他情况的离线方法) 线段树合并&复杂度的简单说明:https://blog.csdn.net/zawedx/article/details ...
- 启发式合并&线段树合并/分裂&treap合并&splay合并
启发式合并 有\(n\)个集合,每次让你合并两个集合,或询问一个集合中是否存在某个元素. 我们可以用平衡树/set维护集合. 对于合并两个\(A,B\),如果\(|A|<|B|\),那么 ...
- CF666E Forensic Examination [后缀自动机,线段树合并]
洛谷 Codeforces 思路 最初的想法:后缀数组+区间众数,似乎并不能过. 既然后缀数组不行,那就按照套路建出广义SAM,然后把\(S\)放在上面跑,得到以每个点结尾会到SAM上哪个节点. 询问 ...
- 【XSY1551】往事 广义后缀数组 线段树合并
题目大意 给你一颗trie树,令\(s_i\)为点\(i\)到根的路径上的字符组成的字符串.求\(max_{u\neq v}(LCP(s_u,s_v)+LCS(s_u,s_v))\) \(LCP=\) ...
- Codeforces.1051G.Distinctification(线段树合并 并查集)
题目链接 \(Description\) 给定\(n\)个数对\(A_i,B_i\).你可以进行任意次以下两种操作: 选择一个位置\(i\),令\(A_i=A_i+1\),花费\(B_i\).必须存在 ...
随机推荐
- 【leetcode】662. Maximum Width of Binary Tree
题目如下: Given a binary tree, write a function to get the maximum width of the given tree. The width of ...
- qt学习(三):鼠标图标改变
qt学习 (三):鼠标图标改变 当你进入一个美好的qt软件场景,比如游戏,电脑的黑白图标会让程序逊色不少, 1改图标要加光标的头文件, 2 载入光标图, 3 再设置改光标就可以了 1在头文件中加 #i ...
- 洛谷P1122 最大子树和 (树状dp)
题目描述 小明对数学饱有兴趣,并且是个勤奋好学的学生,总是在课后留在教室向老师请教一些问题.一天他早晨骑车去上课,路上见到一个老伯正在修剪花花草草,顿时想到了一个有关修剪花卉的问题.于是当日课后,小明 ...
- jdbc 可处理数据库事物的通用增删查改函数
首先弄清四种隔离级别的和三种数据并发 之间的关系 通用查询函数 //使用PreparedStatement实现对不同表的通用的返回一个对象的查询操作 //使用泛型机制,参数里先传入一个类的类型 pub ...
- <自动化测试>之<Selenium API 的用法1>
今天,简单,举例说一下在用python+selenium中元素定位的主要方法,第一部分是单个元素的操作,第二部分是一类元素的操作,实际操作中注意区分 #!/usr/bin/env python # - ...
- thinkphp 连接postgresql
PHP连接: php.ini中将extension=php_pgsql.dll前面的分号去掉extension=php_pdo_pgsql.dll前面的分号去掉,然后设置extension_dir指向 ...
- Tomcat负载均衡、调优核心应用进阶学习笔记(四):JVM调优
文章目录 概述 **JVM管理的内存段可分为两大类:线程共享内存和线程私有内存** Java Heap SIze Options The memory structure of a JVM proce ...
- networkComms 通信框架之 消息处理器
经常做Tcp通信的朋友知道,客户端发送数据到服务器 或者 服务器发送消息到客户端,接收端都要有相对应的处理器来对消息进行处理. 这里有两个概念 需要进行区别 消息类型 实际的数据类型 这里指的是未被 ...
- FPGA前仿真后仿真
前仿真 后仿真 时序(综合后)仿真 时序仿真将时延考虑进去,包括综合后产生的(与.或.非)门时延,还有布局布线产生的时延. 综合(Synthesize),就是将HDL语言设计输入翻译成由与.或.非门和 ...
- PHP中输出字符串(echo,print,printf,print_r和var_dump)的区别【转载】
php中常见的输出语句 echo()可以一次输出多个值,多个值之间用逗号分隔.echo是语言结构(language construct),而并不是真正的函数,因此不能作为表达式的一部分使用. prin ...