[转]我的数据结构不可能这么可爱!——珂朵莉树(ODT)详解
参考资料:
Chtholly Tree (珂朵莉树) (应某毒瘤要求,删除链接,需要者自行去Bilibili搜索)
在全是珂学家的珂谷,你却不知道珂朵莉树?来跟诗乃一起学习珂朵莉树丫~
(挑战用最短的篇幅讲清楚一个毒瘤数据结构)
1、珂朵莉是什么?
珂朵莉·诺塔·瑟尼欧里斯是轻小说及改编动画《末日时在做什么?有没有空?可以来拯救吗?》中的女主角,五位成体妖精兵之一。最强圣剑“瑟尼欧里斯”的适合者。在第28号浮游岛上意外跌落而与威廉相遇,并受到他的帮助。 但是知道了珂朵莉对你学习珂朵莉树并没有什么帮助。
2、珂朵莉树是什么?
珂朵莉树又称老司机树(Old Driver Tree),可能是因为发明者是一个珂学家所以发明了这个数据结构?
废话到此结束。
珂朵莉树是基于C++STL库中的set的数据结构。与线段树、平衡树等树形结构类似,珂朵莉树是用来解决区间问题的很暴力的树形结构。
特色:珂朵莉树支持区间推平(将区间$L$~$R$全部赋值为同一个值$x$)
3、在学习之前你需要知道:set(若熟悉此STL请跳过)
set的本质是一棵平衡树。特性为插入到set中的元素会被自动排序并且不允许set中有相同的元素。
珂朵莉树中需要用到的函数:
set <Node> t; //定义一个名为t,类型为Node(结构体)的set
s.begin(); //返回指向第一个元素的迭代器(地址)
s.end(); //返回指向最后一个元素的迭代器
s.clear(); //清空s
s.insert(a); //将a插入到s
s.erase(l, r);//将迭代器l~r之间的元素全部删除
s.lower_bound(a); //二分查找a在s中的位置,返回一个迭代器。
set<Node>::iterator pos; //定义一个迭代器pos。
4、珂朵莉树可以解决什么问题?
当我们需要推平一段区间时,可以使用珂朵莉树。珂朵莉树高效的基础是数据随机生成。也就是说,这个数据结构很容易被构造数据卡掉。但是在珂朵莉树依然鲜为人知的现在又有谁会去构造数据去卡一个知名度并不高的算法呢?你都愿意学SPFA了不是吗
来看一道例题:
CF869C Willem, Chtholly and Seniorious
题目大意:
我有一个可爱的序列。你需要编写程序支持以下操作:
1 l r x :将[l,r]区间所有数加上x
2 l r x :将[l,r]区间所有数改成x
3 l r x :输出将[l,r] 区间从小到大排序后的第x个数是的多少
4 l r x y :输出[l,r] 区间每个数字的$x$次方的和模y的值
5、珂朵莉树的构造
其实珂朵莉树被称作“树”大概仅仅是因为使用了set吧。在学习珂朵莉树时,你大可以不把它作为一个树来理解。
珂朵莉树的每一个节点由一个三元组(l, r, val)组成,表示区间$[l,r]$之间的值全是x。
例子: 我有一个可爱的序列:
1 1 1 1 1 2 2 2 2 3 3 3 3
则在珂朵莉树上这个序列被表示为(1, 5, 1) (6, 9, 2) (10, 14, 3)三个节点。
建树实现:
struct node {
int l, r;//区间左端点与右端点。
mutable lint v;//mutable为“可变的”,使我们可以直接修改v的值
node(int L, int R = -1, lint V = 0) : l(L), r(R), v(V) {}
bool operator < (const node &o) const {
return l < o.l;
}
}; //定义一个结构体,重载<为按左端点排序。
set <node> s;//扔到set里
是不是非常的简单好懂鸭?
6、核心操作:区间分割
考虑刚刚那个可爱的序列:
1 1 1 1 1 2 2 2 2 3 3 3 3
现在我要强人锁男将2~13这个区间全部加上1。
前文提到,在珂朵莉树上这个序列被表示为(1, 5, 1) (6, 9, 2) (10, 14, 3)三个节点。也就是说我们根本没有办法直接对2~13这个区间加上1.
这个时候如果我们将(1, 5, 1) (6, 9, 2) (10, 14, 3)三个区间分割为(1, 1, 1) (2, 5, 1) (6, 9, 2) (9, 13, 3) (14, 14, 3)这几个区间就可以直接对区间2~13中所对应的节点直接进行操作啦!超快乐!
∴现在我们需要写一个函数spilit(pos),将pos所在的节点以pos为中心分为两个节点。
实现步骤:
- 找到pos所在的节点(l, r, val)
- 删掉这个节点
- 把这个节点变成(l, pos-1, val)和(pos, r, val)扔回set。
- 返回右区间迭代器(以后有用)
代码实现:
#define IT set<node>::iterator
//迭代器宏定义
IT spilit (int pos) {
IT it = s.lower_bound(node(pos, -1, 0));//找到第一个l不小于pos的节点
if(it != s.end() && it->l == pos) return it;//不需要分割直接退出
it--;//pos一定在前一个区间中
int L = it -> l, R = it -> r;
lint V = it->v;
s.erase(it);//删了
s.insert(node(L, pos-1, V));//左区间丢进去
return s.insert(node(pos, R, V)).first;//右区间丢进去,返回右区间的迭代器。
}
是不是特别简单?
7、降低复杂度的关键:区间赋值(推平)
实现步骤: 设要推平的区间为[l, r]。
- 把l和r所在的节点分割。
- 把要推平的区间[l, r]之间的节点全部删掉。
- 把(l, r, val)扔进set
没了。诗乃觉得没有什么需要解释的了。
#define IT set<node>::iterator
void tp(int l, int r, int val) {
IT il = spilit(l), ir = spilit(r+1);
s.erase(il, ir);
s.insert(node(l, r, val));
}
8、当我们要对一段区间进行操作时:
设要操作的区间为[l, r]。
我们先把l和r所在的节点分割,暴力对区间[l, r]中的节点一个个取出来操作(修改或统计答案)即可,反正没有多少节点。这个东西视具体题目而定,这里不再赘述。
例子:区间加
#define IT set<node>::iterator
void add(int l, int r, int val) {
IT il = spilit(l), ir = spilit(r+1);
for(; il != ir; il->v += val, il++);
}
9、我最喜欢暴力数据结构了。
惊不惊喜?现在你已经学会珂朵莉树了,可以做一做例题练一下手呢。(是NOI+/CTSC难度的黑题哦)。
其他例题: CF915E 、bzoj4293(权限题)
珂朵莉树的优点:码量少,好理解,调错快。
缺点:在数据随机的时候推平操作比较多,所以它的复杂度会趋近于mlogn(m为询问次数)。但当出题人想要卡珂朵莉树时肯定会T飞。珂朵莉这么可爱谁会卡呢
10、我永远喜欢珂朵莉!
[转]我的数据结构不可能这么可爱!——珂朵莉树(ODT)详解的更多相关文章
- [数据结构]ODT(珂朵莉树)实现及其应用,带图
[数据结构]ODT(珂朵莉树)实现及其应用,带图 本文只发布于博客园,其他地方若出现本文均是盗版 算法引入 需要一种这样的数据结构,需要支持区间的修改,区间不同值的分别操作. 一般的,我们会想到用线段 ...
- Comet OJ - Contest #14 转转的数据结构题 珂朵莉树+树状数组
题目链接: 题意:有两个操作 操作1:给出n个操作,将区间为l到r的数字改为x 操作2:给出q个操作,输出进行了操作1中的第x到x+y-1操作后的结果 解法: 把询问离线,按照r从小到大排序 每次询问 ...
- 数据结构31:树(Tree)详解
复制广义表数据结构中的树 树是数据结构中比较重要也是比较难理解的一类存储结构.本章主要主要围绕二叉树,对树的存储以及遍历做详细的介绍,同时还会涉及到有关树的实际应用,例如构建哈弗曼编码等. 由于树存储 ...
- Redis数据结构详解之List(二)
序言 思来想去感觉redis中的list没什么好写的,如果单写几个命令的操作过于乏味,所以本篇最后我会根据redis中list数据类型的特殊属性,同时对比成熟的消息队列产品rabbitmq,使用red ...
- 数据结构图文解析之:队列详解与C++模板实现
0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...
- 数据结构图文解析之:AVL树详解及C++模板实现
0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...
- 数据结构图文解析之:二叉堆详解及C++模板实现
0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...
- 数据结构图文解析之:哈夫曼树与哈夫曼编码详解及C++模板实现
0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...
- redis 五种数据结构详解(string,list,set,zset,hash)
redis 五种数据结构详解(string,list,set,zset,hash) Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存 ...
随机推荐
- (转)Java随机数
1 随机数的三种产生方式 本章先讲解Java随机数的几种产生方式,然后通过示例对其进行演示. 广义上讲,Java中的随机数的有三种产生方式: (01). 通过System.currentTimeMil ...
- Java基础-反射(reflect)技术详解
Java基础-反射(reflect)技术详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.类加载器 1>.JVM 类加载机制 如下图所示,JVM类加载机制分为五个部分 ...
- python制作查找单词翻译的脚本
本人由于英语渣,在linux底下经常看文档,但是有没有想有道词典这种软件,所以遇到不懂的单词只能手动复制粘贴在网上查找,这样就很不方便,学了python之后,就试着自己尝试下个在命令行下查找单词翻译的 ...
- springMvc + Maven 项目提示 hessian 依赖包 无法下载;
首先 从 https://github.com/alibaba/dubbo/archive/master.zip 下载最新的 dubbo 源码包到本地某个目录, 解压出来: cmd 进入该目录: 执行 ...
- PHP复制文件夹及文件夹内的文件
//1.取被复制的文件夹的名字://2.写出新的文件夹的名字://3.调用此函数,将旧.新文件夹名字作为参数传递://4.如需复制文件夹内的文件,第三个参数传1,否则传0: public functi ...
- 进程ID[PID(Process ID)]与端口号[(Port ID)]的联系
1.首先声明一点:PID不是端口(port id),而是Process ID进程号的意思. 2.那么,什么是进程号? 采集网友的意见就是: 进程号,是系统分配给么一个进程的唯一标识符.PID就是各进程 ...
- [BZOJ 2257][JSOI2009]瓶子和燃料 题解(GCD)
[BZOJ 2257][JSOI2009]瓶子和燃料 Description jyy就一直想着尽快回地球,可惜他飞船的燃料不够了. 有一天他又去向火星人要燃料,这次火星人答应了,要jyy用飞船上的瓶子 ...
- 将本地的mongodb迁移到阿里云
首先在阿里云上安装mongodb,可以根据官方教程 https://docs.mongodb.com/manual/tutorial/install-mongodb-on-amazon/ 完成之后启动 ...
- 自己动手开发Socks5代理服务器
一.Socks5协议简介 socks5是基于传输层的协议,客户端和服务器经过两次握手协商之后服务端为客户端建立一条到目标服务器的通道,在传输层转发TCP/UDP流量. 关于socks5协议规范,到处都 ...
- 洛谷 P4656: LOJ 2484: [CEOI2017]Palindromic Partitions
菜菜只能靠写简单字符串哈希维持生活. 题目传送门:LOJ #2484. 题意简述: 题面讲得很清楚了. 题解: 很显然从两边往中间推,能选的就选上这个贪心策略是对的. 如何判断能不能选上,直接字符串哈 ...