[luogu3380][bzoj3196]【模板】二逼平衡树【树套树】
题目地址
题目大意
区间查询k的排名,查找k排名的数,单点修改,区间前驱,区间后继。
感想
真的第一次写树套树,整个人都不对了。重构代码2次,发现样例都过不了,splay直接爆炸,可能是我太弱了。
换了treap,就过掉了。
但是理解树套树的思路也花了我不少的时间。
分析
很多人都不知道树套树是什么东西,我一开始也是很懵逼的。
为什么两棵树可以套在一起??这是什么操作??
但是HG的xyc大佬给我点波了一句话,我就明白了。
因为原来的线段树中只有tag一个标记,我们记录的东西就太少了。也不方便操作,那么我们就将这个线段树的所有节点都换成一棵平衡树就可以了。
如果有时间我一定会写一个树套树的详细总结(flag)。
但是我们不是对每一个节点都建一个关于整体的平衡树的节点,这样复杂度太高了,我们就将这个区间内的所有点都扔到一个平衡树中,是不是非常暴力呢??
其实这样的做法,仅仅增加了空间复杂度\(log_2n \times n\)的空间,因为我们需要建多棵平衡树,所以我们一开始就准备一大堆的空节点,如果有需要的,那么就直接从空节点中拿一个就可以了,
那么关于时间复杂度,其实也就只是增加了一个平衡树的单次操作的\(log\)。
但是有一个例外,就是操作2。
因为我们需要查找排名k的数,那么我们选择二分答案,每一次枚举rank,然后检验是否在k内,那么操作2还需要一个log的复杂度,所以差不多就是\(log_n^3 \times n\)的复杂度。
代码
#include <bits/stdc++.h>
#define inf 2147483647
#define lc (nod << 1)
#define rc (nod << 1 | 1)
#define N 500004
using namespace std;
template <typename T>
inline void read(T &x) {
x = 0; T fl = 1;
char ch = 0;
while (ch < '0' || ch > '9') {
if (ch == '-') fl = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
x *= fl;
}
struct node {
int val, cnt, ch[2], sz, rd;
void init(int x) {val = x; cnt = sz = 1; ch[1] = ch[0] = 0; rd = rand() % 100;}
}tr[N * 20];
int tot;
struct Treap {
#define ls tr[nod].ch[0]
#define rs tr[nod].ch[1]
void pushup(int nod) {
tr[nod].sz = tr[ls].sz + tr[rs].sz + tr[nod].cnt;
}
void rotate(int &nod, int d) {
int k = tr[nod].ch[d];
tr[nod].ch[d] = tr[k].ch[d ^ 1];
tr[k].ch[d ^ 1] = nod;
pushup(nod);
pushup(k);
nod = k;
}
void ins(int &nod, int val) {
if (!nod) {
tr[nod = ++ tot].init(val);
return;
}
tr[nod].sz ++;
if (tr[nod].val == val) {
tr[nod].cnt ++;
return;
}
int d = val > tr[nod].val;
ins(tr[nod].ch[d], val);
if (tr[nod].rd > tr[tr[nod].ch[d]].rd) rotate(nod, d);
}
void del(int &nod, int val) {
if (!nod) return;
if (tr[nod].val == val) {
if (tr[nod].cnt > 1) {
tr[nod].cnt --;
tr[nod].sz --;
return;
}
int d = tr[ls].rd > tr[rs].rd;
if (ls == 0 || rs == 0) nod = ls + rs;
else rotate(nod, d), del(nod, val);
}
else tr[nod].sz --, del(tr[nod].ch[tr[nod].val < val], val);
}
int rk(int nod, int val) {
if (!nod) return 0;
if (tr[nod].val == val) return tr[ls].sz;
if (tr[nod].val > val) return rk(ls, val);
else return tr[ls].sz + tr[nod].cnt + rk(rs, val);
}
int kth(int nod, int k) {
while (233) {
if (k <= tr[ls].sz) nod = ls;
else if (k > tr[ls].sz + tr[nod].cnt) k -= tr[ls].sz + tr[nod].cnt, nod = rs;
else return tr[nod].val;
}
}
int pre(int nod, int val) {
if (!nod) return -inf;
if (tr[nod].val >= val) return pre(ls, val);
else return max(tr[nod].val, pre(rs, val));
}
int suc(int nod, int val) {
if (!nod) return inf;
if (tr[nod].val <= val) return suc(rs, val);
else return min(tr[nod].val, suc(ls, val));
}
}tp[N << 2];
int n, m;
int a[N], rt[N];
void build(int nod, int l, int r) {
for (int i = l; i <= r; i ++) tp[nod].ins(rt[nod], a[i]);
if (l == r) return;
int mid = (l + r) >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
}
int query_rk(int nod, int l, int r, int ql, int qr, int k) {
if (l > qr || r < ql) return 0;
if (ql <= l && r <= qr) return tp[nod].rk(rt[nod], k);
int mid = (l + r) >> 1, res = 0;
res += query_rk(lc, l, mid, ql, qr, k);
res += query_rk(rc, mid + 1, r, ql, qr, k);
return res;
}
void update_point(int nod, int l, int r, int k, int val) {
if (k < l || k > r) return;
tp[nod].del(rt[nod], a[k]);
tp[nod].ins(rt[nod], val);
if (l == r) return;
int mid = (l + r) >> 1;
update_point(lc, l, mid, k, val);
update_point(rc, mid + 1, r, k, val);
}
int query_pre(int nod, int l, int r, int ql, int qr, int k) {
if (l > qr || r < ql) return -inf;
if (ql <= l && r <= qr) return tp[nod].pre(rt[nod], k);
int mid = (l + r) >> 1, res = query_pre(lc, l, mid, ql, qr, k);
res = max(res, query_pre(rc, mid + 1, r, ql, qr, k));
return res;
}
int query_suc(int nod, int l, int r, int ql, int qr, int k) {
if (l > qr || r < ql) return inf;
if (ql <= l && r <= qr) return tp[nod].suc(rt[nod], k);
int mid = (l + r) >> 1, res = query_suc(lc, l, mid, ql, qr, k);
res = min(res, query_suc(rc, mid + 1, r, ql, qr, k));
return res;
}
int query_fd(int ql, int qr, int k) {
int l = 0, r = 1e8, res = -1;
while (l <= r) {
int mid = (l + r) >> 1;
if (query_rk(1, 1, n, ql, qr, mid) + 1 <= k) res = mid, l = mid + 1;
else r = mid - 1;
}
return res;
}
int main() {
srand(time(NULL));
tot = 0;
read(n); read(m);
for (int i = 1; i <= n; i ++) read(a[i]);
build(1, 1, n);
for (int i = 1; i <= m; i ++) {
int opt, x, y, z;
read(opt);
if (opt == 1) {
read(x); read(y); read(z);
printf("%d\n", query_rk(1, 1, n, x, y, z) + 1);
}
if (opt == 2) {
read(x); read(y); read(z);
printf("%d\n", query_fd(x, y, z));
}
if (opt == 3) {
read(x); read(y);
update_point(1, 1, n, x, y);
a[x] = y;
}
if (opt == 4) {
read(x); read(y); read(z);
int res = query_pre(1, 1, n, x, y, z);
printf("%d\n", res);
}
if (opt == 5) {
read(x); read(y); read(z);
int res = query_suc(1, 1, n, x, y, z);
printf("%d\n", res);
}
}
return 0;
}
[luogu3380][bzoj3196]【模板】二逼平衡树【树套树】的更多相关文章
- [BZOJ3196] [Tyvj1730] 二逼平衡树(线段树 套 Splay)
传送门 至少BZOJ过了,其他的直接弃. 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 1.查询k在区间内的排名 2.查询区间内排名为k的值 3.修改某一位值上的 ...
- BZOJ3196 Tyvj1730 二逼平衡树 【树套树】 【线段树套treap】
BZOJ3196 Tyvj1730 二逼平衡树 Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 1.查询k在区间内的排名 2.查询区间内排名 ...
- 【BZOJ3196】二逼平衡树(树状数组,线段树)
[BZOJ3196]二逼平衡树(树状数组,线段树) 题面 BZOJ题面 题解 如果不存在区间修改操作: 搞一个权值线段树 区间第K大--->直接在线段树上二分 某个数第几大--->查询一下 ...
- [BZOJ3196][Tyvj1730]二逼平衡树
[BZOJ3196][Tyvj1730]二逼平衡树 试题描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 查询 \(k\) 在区间内的排名 查询区间内排名为 \ ...
- 【BZOJ-3196】二逼平衡树 线段树 + Splay (线段树套平衡树)
3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2271 Solved: 935[Submit][Stat ...
- [bzoj3196][Tyvj1730]二逼平衡树_树套树_位置线段树套非旋转Treap/树状数组套主席树/权值线段树套位置线段树
二逼平衡树 bzoj-3196 Tyvj-1730 题目大意:请写出一个维护序列的数据结构支持:查询给定权值排名:查询区间k小值:单点修改:查询区间内定值前驱:查询区间内定值后继. 注释:$1\le ...
- bzoj 3196 Tyvj 1730 二逼平衡树(线段树套名次树)
3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1807 Solved: 772[Submit][Stat ...
- bzoj 3196/ Tyvj 1730 二逼平衡树 (线段树套平衡树)
3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec Memory Limit: 128 MB[Submit][Status][Discuss] Description ...
- BZOJ3196:二逼平衡树
浅谈树状数组与线段树:https://www.cnblogs.com/AKMer/p/9946944.html 浅谈\(Splay\):https://www.cnblogs.com/AKMer/p/ ...
- BZOJ3196 二逼平衡树 ZKW线段树套vector(滑稽)
我实在是不想再打一遍树状数组套替罪羊树了... 然后在普通平衡树瞎逛的时候找到了以前看过vector题解 于是我想:为啥不把平衡树换成vector呢??? 然后我又去学了一下ZKW线段树 就用ZKW线 ...
随机推荐
- Python进阶之函数式编程
函数式编程 函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计.函数就是面向过程的程序设计 ...
- 下拉框 JComboBox,文本框JTextField
1. 下拉框 JComboBox //导入Java类 import javax.swing.*; import java.awt.*; import java.awt.event.ActionEven ...
- Android开发,关于如何在应用间共享SharedPreference
开发一个应用,需要用到两个应用A和B之间共享数据的问题,这个数据是个单一的数据,所以就想用SharedPrefernce来进行保存. 使用网上的各种应用间的共享代码,B是读取A的数据,所以代码为: C ...
- spring学习总结——高级装配学习一(profile与@Conditional)
前言: 在上一章装配Bean中,我们看到了一些最为核心的bean装配技术.你可能会发现上一章学到的知识有很大的用处.但是,bean装配所涉及的领域并不仅仅局限于上一章 所学习到的内容.Spring提供 ...
- GDB 显示别的文件;在别文件打断点;执行到函数末尾;跳出当前函数
显示别的文件:l "文件名.后缀名":行号 在别文件打断点:b "文件名.后缀名":行号 执行到函数末尾:finish 跳出当前函数(当前位置到函数的末尾不被执 ...
- 利用ZYNQ SOC快速打开算法验证通路(2)——数据传输最简方案:网络调试助手+W5500协议栈芯片
在上一篇该系列博文中讲解了MATLAB待处理数据写入.bin二进制数据文件的过程,接下来需要将数据通过以太网发送到ZYNQ验证平台.之前了解过Xilinx公司面向DSP开发的System Genera ...
- Docker之进入容器(三)
1.简介 经过前面两篇博客的扫盲,大家多多少少对docker有了一个基本的了解,也接触了docker的常用命令.在这篇博客中,我将介绍进入docker容器的几种方式. 2.进入docker中的几种方式 ...
- Django--session(登录用)
一.session的原理图 二.Django中session对象的设置/读取/删除及其他方法 三. Django--配置 settings.py中与session有关的参数 一.session的原理图 ...
- linux 本机内核模块
make -C /lib/modules/4.13.0-36-generic/build M=/$(pwd) modules 内核里只需要写obj-m:=hello.o
- zookeeper的分布式锁
实现分布式锁目前有三种流行方案,分别为基于数据库.Redis.Zookeeper的方案,其中前两种方案网络上有很多资料可以参考,本文不做展开.我们来看下使用Zookeeper如何实现分布式锁. 什么是 ...