【学习笔记】珂朵莉树/颜色段均摊
# 壹:【知识点】
一:【定义】
珂朵莉树(Chtholly Tree)也叫老司机树(ODT),是一种基于平衡树(一般用 set 或 map 实现)的维护颜色段均摊的暴力技巧,而非一种数据结构。
其核心思想是将值相同的一段区间合并成一个结点处理.相较于传统的线段树等数据结构,对于含有区间覆盖的操作的问题,珂朵莉树可以更加方便地维护每个被覆盖区间的值.可用于解决区间修改、区间加法、区间查询以及其他区间操作,在处理区间修改效率最高,适用于随机数据。
二:【节点类型】
struct node{
int l,r,v;//左边界,右边界,颜色
const bool operator<(const node b)const{
return l<b.l;
}
};
三:【节点存储】
我们希望维护所有结点,使得这些结点所代表的区间左端点单调增加且两两不交,最好可以保证所有区间的并是一个极大的连续范围,此处用set维护区间
初始化时,向珂朵莉树中插入一个极长区间
四:【split操作】
split操作是ODT的核心,它接受一个 \(x\) ,把一个包含 \(x\) 的区间 \([l,r]\) 切分为 \([l,x)\) 和 \([x,r]\) ,并返回后者的迭代器
auto split(int x) {
auto it=odt.lower_bound(Node(x, 0, 0));
if (it!=odt.end()&&it->l==x) return it;//x=l不需要切分
--it;//此处it是[l,r]的迭代器
int l=it->l,r=it->r,v=it->v;
odt.erase(it);
odt.insert(node(l,x-1,v));
return odt.insert(node(x,r,v)).first;//insert的返回值是{插入元素的迭代器,插入是否成功}
}
//返回值也可以是set<node>::iterator
五:【assign操作】
另外一个重要的操作:assign 表示对区间 \([l,r]\) 赋值为 \(v\)
void assign(int l,int r,int v) {
auto itr=split(r + 1),itl= split(l);
odt.erase(itl,itr);
odt.insert(node(l, r, v));
}
六:【perform操作】
将珂朵莉树上的一段区间提取出来并进行操作.与 assign 操作类似,只不过是将删除区间改为遍历区间.
void perform(int l, int r) {
auto itr=split(r+1),itl=split(l);
for(;itl!= itr;++itl){
//...
}
}
七:【EX】
map维护ODT懒得学,听说更简单??
# 贰:【例题】
一:【P3740 [HAOI2014] 贴海报】
除了ODT,离散化+线段树,并查集模拟链表也可以水过
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
struct node{
int l,r,v;
node(int a,int b,int c){
l=a;r=b;v=c;
}
const bool operator<(const node b)const{
return l<b.l;
}
};
set<node> odt;
auto sprit(int x){
auto it=odt.lower_bound(node(x,0,0));
if(it!=odt.end()&&it->l==x) return it;
it--;
int l=it->l,r=it->r,v=it->v;
odt.erase(it);
odt.insert(node(l,x-1,v));
return odt.insert(node(x,r,v)).first;
}
void assign(int l,int r,int v){
auto itr=sprit(r+1),itl=sprit(l);
odt.erase(itl,itr);
odt.insert(node(l,r,v));
}
int main(){
int n,m;cin>>n>>m;
odt.insert({1,n,0});
for(int i=1;i<=m;i++){
int l,r;cin>>l>>r;
assign(l,r,i);
}
set<int> ans;
for(auto it:odt) ans.insert(it.v);
cout<<ans.size()-ans.count(0)<<"\n";
return 0;
}
二:【P1840 Color the Axis】
线段树,分块也可以AC
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
struct node{
int l,r,v;
node(int a,int b,int c){
l=a;r=b;v=c;
}
const bool operator<(const node b)const{
return l<b.l;
}
};
set<node> odt;
auto sprit(int x){
auto it=odt.lower_bound(node(x,0,0));
if(it!=odt.end()&&it->l==x) return it;
it--;
int l=it->l,r=it->r,v=it->v;
odt.erase(it);
odt.insert(node(l,x-1,v));
return odt.insert(node(x,r,v)).first;
}
void assign(int l,int r,int v){
auto itr=sprit(r+1),itl=sprit(l);
odt.erase(itl,itr);
odt.insert(node(l,r,v));
}
int perform(int l,int r){
auto itr=sprit(r+1),itl=sprit(l);
int res=0;
while(itl!=itr){
res+=(itl->r-itl->l+1)*(!itl->v);
itl++;
}
return res;
}
int main(){
int n,m;cin>>n>>m;
odt.insert(node(1,n,0));
int ans=n;
while(m--){
int l,r;cin>>l>>r;
ans-=perform(l,r);
assign(l,r,1);
cout<<ans<<"\n";
}
return 0;
}

浙公网安备 33010602011771号