前言:
珂朵莉树($ODT$),与其说它是数据结构,不如说它是暴力
它可以代替线段树实现某些区间操作
复杂度嘛 $O($不对$)$ 吧
正文:
SET
要学珂朵莉树,首先要会使用 $STL$ 的 $set$
$set$ 是一个集合,它会将其中的元素自动排序与去重
$ps:$ 如果需要可重集,请使用 $multiset$
比较常用的函数有
$s.insert()$ 在集合中插入此元素
$s.erase()$ 删除集合中的元素
$s.size()$ 返回集合中的元素个数
$s.empty()$ 如果集合为空返回 1,否则返回 0
$s.clear()$ 清除所有元素
$s.begin()$ 返回指向第一个元素的迭代器
$s.end()$ 返回指向最后一个元素的迭代器
$s.find()$ 在集合中查找此元素,如找到则返回该元素的迭代器,否则返回 $s.end()$
$s.upper\_bound()$ 返回集合中二分查找到的第一个大于此元素的迭代器
$s.lower\_bound()$ 返回集合中二分查找到的第一个大于等于此元素的迭代器
想看更详细的 $set$ 介绍,请点击这里
接下来可以搞珂朵莉珂朵莉树了
节点信息
首先我们来看 $set$ 中的每个节点所维护的信息
struct node { int l,r; mutable ll v; node(int l,int r=-1,ll v=0):l(l),r(r),v(v){} bool operator < (const node &a) const { return l<a.l; } };
每一个 $node$ 维护了 3 个信息 $l,r,v$
它的含义是 $l$ 到 $r$ 区间的权值都为 $v$
$mutable$ 声明之后,我们将可以修改这个权值 $v$,否则会 $CE$
然后我们重载一下,按 $l$ 从小到大排序,并扔到 $set$ 里
set<node>s; #define IT set<node>::iterator
核心操作
接下来是珂朵莉树的核心操作
IT split(int pos) { IT it=s.lower_bound(node(pos));//找到首个l不小于pos的节点 if(it!=s.end()&&it->l==pos) return it;//如果pos是某个节点的左端点,直接返回该节点 it--;//否则pos一定在前一个区间中 int l=it->l,r=it->r;//[l,r]就是要被分割的区间 ll v=it->v;//取出这个节点的值 s.erase(it);//删除原节点 s.insert(node(l,pos-1,v));//插入前半段 return s.insert(node(pos,r,v)).first;//插入后半段,返回后半段的迭代器 }
这个操作的作用是将原来含有 $pos$ 位置的节点切为两段区间 $[l,pos-1]$ 和 $[pos,r]$
关于最后的返回值,$SuperJvRuo$ 说是利用了 pair<iterator,bool> insert (const value_type& val) 的返回值
我不知道,我一定不知道
void assign_num(int l,int r,ll num) { IT itr=split(r+1),itl=split(l); s.erase(itl,itr); s.insert(node(l,r,num)); }
这个操作可以推平一段区间
先把 $l$ 和 $r+1$ 进行 $split$
注意这里要先切 $r+1$ 后切 $l$
然后我们用 $s.erase()$ 删除 $[l,r+1)$ 之间的节点
再插入节点 $node(l,r,num)$
这样就可以将 $[l,r]$ 区间的权值全部修改为 $num$
此操作可以保证珂朵莉树的复杂度
当然是在数据随机的情况下
其他操作
区间加
void add(int l,int r,ll num) { IT itr=split(r+1),itl=split(l); for(;itl!=itr;itl++) itl->v+=num; }
区间求和
ll get_sum(int l,int r) { ll ans=0; IT itr=split(r+1),itl=split(l); for(;itl!=itr;itl++) ans+=itl->v*1LL*(itl->r-itl->l+1); return ans; }
区间第 $k$ 大
ll get_kth(int l,int r,int k) { vector< pair<ll,int> >vec; IT itr=split(r+1),itl=split(l); for(;itl!=itr;itl++) vec.push_back(pair<ll,int>(itl->v,itl->r-itl->l+1)); std::sort(vec.begin(),vec.end()); for(vector< pair<ll,int> >::iterator it=vec.begin();it!=vec.end();it++) { k-=it->second; if(k<=0) return it->first; } }
这些都是一些极其暴力的操作
不过是真的好写好理解啊~~~
后序:
关于它的名字($Old Driver Tree$)
什么老司机树,旧驱动树啊
我并不知道这些都是怎么来的
还有考试的时候好像一般都不敢写珂朵莉树
虽然它好写,但它可以用来骗分啊!!!