1 引入
最近看jiangly的CF1638E代码,发现jiangly的珂朵莉树是用map实现的。于是研究学习了一下。
如果没有接触过珂朵莉树,请先学习下面的内容。
2 珂朵莉树的map实现原理
珂朵莉树的set实现中,一个区间被记为 [ l , r , v a l ] [l, r, val] [l,r,val] ,分别表示区间左端点,区间右端点,和区间所储存的值。
但是,由于珂朵莉树的区间和区间时相邻的,本区间的右端点一定为下一个区间的左端点,所以我们可以将一个区间简单记为 [ l , v a l ] [l, val] [l,val] ,分别表示区间的左端点和区间所储存的值。
这样,我们就可以直接用标准库里map,对于一个区间,我们将 l l l 记为map里的key值,将 v a l val val 记为map里的value值。这样写起来更容易,代码量也更小。
3 珂朵莉树的map实现方法
先直接上完整代码。
struct ODT {
const int n;
map<int, int> mp;
ODT(int n) : n(n) { mp[-1] = 0 }
void split(int x) {
auto it = prev(mp.upper_bound(x)); //找到左端点小于等于x的区间
mp[x] = it->second; //设立新的区间,并将上一个区间储存的值复制给本区间。
}
void assign(int l, int r, int v) { // 注意,这里的r是区间右端点+1
split(l);
split(r);
auto it = mp.find(l);
while (it->first != r) {
it = mp.erase(it);
}
mp[l] = v;
}
void update(int l, int r, int c) { // 其他操作
split(l);
split(r);
auto it = mp.find(l);
while (it->first != r) {
// 根据题目需要做些什么
it = next(it);
}
}
};
然后我们逐一进行讲解
3.1 初始化操作
设区间下标为
[
0
,
n
−
1
]
[0, n - 1]
[0,n−1]。
由于一开始只有一个区间,即
[
0
,
n
−
1
]
[0, n - 1]
[0,n−1],那么我们初始化时,只用插入一个左端点为
0
0
0 的区间。
ODT(int n) : n(n) { mp[-1] = 0 }
3.2 split操作
假设我们需要以 x x x 为左端点建立新区间,我们直接在map中找到左端点小于等于 x x x 的区间,设立新的区间,并将上一个区间储存的值复制给本区间。
void split(int x) {
auto it = prev(mp.upper_bound(x)); //找到左端点小于等于x的区间
mp[x] = it->second; //设立新的区间,并将上一个区间储存的值复制给本区间。
}
3.3 assign操作
对于assign操作,我们需要把 [ l , r − 1 ] [l, r-1] [l,r−1]内所有区间左端点删除,再建立新的区间。
void assign(int l, int r, int v) { // 注意,这里的r是区间右端点+1
split(l);
split(r);
auto it = mp.find(l);
while (it->first != r) {
it = mp.erase(it);
}
mp[l] = v;
}
};
3.4 其他操作
void update(int l, int r, int c) { // 其他操作
split(l);
split(r);
auto it = mp.find(l);
while (it->first != r) {
// 根据题目需要做些什么
it = next(it);
}
}
4 CF1638E的珂朵莉树实现代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using ldb = long double;
#define int i64
#define double ldb
template <typename T>
struct BIT {
const int n;
std::vector<T> a;
BIT(int n) : n(n), a(n) {}
void add(int x, T v) {
for (int i = x + 1; i <= n; i += i & -i) {
a[i - 1] += v;
}
}
void rangeAdd(int l, int r, T v) {
add(l, v);
add(r, -v);
}
T sum(int x) {
T ans = 0;
for (int i = x + 1; i > 0; i -= i & -i) {
ans += a[i - 1];
}
return ans;
}
T rangeSum(int l, int r) {
return sum(r) - sum(l);
}
};
struct ODT {
const int n;
map<int, int> mp;
vector<int> lazy;
BIT<int> bit;
ODT(int n) : n(n), lazy(n), bit(n) { mp[-1] = 0; }
void split(int x) {
auto it = prev(mp.upper_bound(x));
mp[x] = it->second;
}
void assign(int l, int r, int v) {
split(l);
split(r);
auto it = mp.find(l);
while (it->first != r) {
it = mp.erase(it);
}
mp[l] = v;
}
void update(int l, int r, int c) {
split(l);
split(r);
auto it = mp.find(l);
while (it->first != r) {
bit.rangeAdd(it->first, next(it)->first, lazy[it->second] - lazy[c]);
it = next(it);
}
}
void add(int c, int x) {
lazy[c] += x;
}
int query(int x) {
split(x);
return bit.sum(x) + lazy[mp[x]];
}
};
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, q;
cin >> n >> q;
ODT odt(n);
for (int i = 0; i < q; i++) {
string op;
cin >> op;
if (op == "Color") {
int l, r, c;
cin >> l >> r >> c;
l--;
c--;
odt.update(l, r, c);
odt.assign(l, r, c);
}
else if (op == "Add") {
int c, x;
cin >> c >> x;
c--;
odt.add(c, x);
}
else if (op == "Query") {
int x;
cin >> x;
x--;
cout << odt.query(x) << "\n";
}
}
return 0;
}