[学习笔记]珂朵莉树
0x00 介绍##
珂朵莉树(Chtholly Tree),又称 ODT(Old Driver Tree),一种数据结构,但似乎暴力到不能称之为数据结构。可以很好地骗分,在随机数据下十分有效,常用于将\([l, r]\) 赋值为 \(k\) 的时候,我们将这个操作称为推平操作
1x00 思想##
前置芝士:set
主要的思想是将值相同的区间放在同一个节点里,保存在 set 中
时间复杂度为 \(O(n \log \log n)\)
1x01 节点保存##
节点代码如下
struct Node{
int l, r;
mutable int val;
Node(int l, int r, int val) : l(l), r(r), val(val){}
bool operator < (const Node a) const{
return l < a.l;
}
};
set<Node> Chtholly;
这其中,mutable
十分成功地吸引了我们的注意力,mutable
意为「可变的」,因为在 set 中,所有的变量都是常量,若要修改某个值,需要取出再放回去,加上了 mutable
,就可以突破这个限制,达到直接修改某个值的目的。
1x02 核心操作 split
##
这里先给出代码
auto split(int x)
{
auto it = Chtholly.lower_bound(Node(x, 0, 0));
if (it != Chtholly.end() && it -> l == x)return it;
it--;
if (it -> r < x)return Chtholly.end();
int l = it -> l, r = it -> r, val = it -> val;
Chtholly.erase(it);
Chtholly.insert(Node(l, x - 1, val));
return Chtholly.insert(Node(x, r, val)).first;
}
split
第一段代码是为了找出第一个 \(x \le l\) 的元素,如果这个元素的 \(l = x\) 时,直接返回找到的。
然后 it--
, 若下一个元素的 \(r > x\),那么返回 set 的结尾。
接下来就是将他拆分开来,拆分成 \([l,x)\) 和 \([x,r]\),先删除原节点,再加入\([l,x)\) 和 \([x,r]\) 这两个新节点。
split
操作的意义在于它可以将这些序列 \([l,r]\) 的操作转换为 set 中 \([split(l),split(r+1))\),的操作。
1x03 推平操作 assign
##
理解了上面的代码,这里就应该很好理解了吧
void assign(int l, int r, int val)
{
auto itl = split(l), itr = split(r + 1);
for (auto it = itl; it != itr; it++)it -> val = val;
}
2x00 例题##
2x01 例题一 CF896C##
2x01x01 分析##
万物之源。
操作 1 为区间加,像推平操作那样类似的方式即可
操作 2 为推平,直接用
操作 3 为区间第 \(k\) 小,用数组保存下这个区间的节点,然后询问
操作 4 为区间所有数的 \(x\) 次方之和 \(\bmod \ y\),用快速幂解决掉
2x01x02 代码##
/*Code By Manipula*/
#include <bits/stdc++.h>
// #define Fileopen
// #pragma GCC optimize(2)
#define INF 0x3f3f3f3f
#define int long long
#define FOR(i, a, b) for (int i = a; i <= b; i++)
#define ROF(i, a, b) for (int i = a; i >= b; i--)
#define swap(x, y) x ^= y ^= x ^= y
#define sqrt(n) pow(n, 0.5)
#define sort stable_sort
#define mst(arr, num) memset(arr, num, sizeof(arr))
#define endl '\n'
using namespace std;
struct Node{
int l, r;
mutable int val;
Node(int l, int r, int val) : l(l), r(r), val(val){}
bool operator < (const Node a) const{
return l < a.l;
}
};
struct Rank{
int val, num;
Rank(int val, int num) : val(val), num(num){}
bool operator < (const Rank a) const{
return val < a.val;
}
};
set<Node> Chtholly;
vector<Rank> RK;
auto split(int x)
{
auto it = Chtholly.lower_bound(Node(x, 0, 0));
if (it != Chtholly.end() && it -> l == x)return it;
it--;
if (it -> r < x)return Chtholly.end();
int l = it -> l, r = it -> r, val = it -> val;
Chtholly.erase(it);
Chtholly.insert(Node(l, x - 1, val));
return Chtholly.insert(Node(x, r, val)).first;
}
void assign(int l, int r, int val)
{
auto itr = split(r + 1), itl = split(l);
Chtholly.erase(itl, itr);
Chtholly.insert(Node(l, r, val));
}
void add(int l, int r, int val)
{
auto itr = split(r + 1), itl = split(l);
for (auto it = itl; it != itr; it++)it -> val += val;
}
int rk(int l, int r, int x)
{
RK.clear();
auto itr = split(r + 1), itl = split(l);
for (auto it = itl; it != itr; it++)
RK.push_back(Rank(it -> val, it -> r - it -> l + 1));
sort(RK.begin(), RK.end());
for (auto it = RK.begin(); it != RK.end(); it++)
if (x > it -> num)x -= it -> num;
else return it -> val;
return INF;
}
int qpow(int n, int k, int mod)
{
int res = 1, rhs = n % mod;
for (; k; k >>= 1, rhs = (rhs * rhs) % mod)
if (k & 1)res = (res * rhs) % mod;
return res;
}
int query(int l, int r, int x, int mod)
{
auto itr = split(r + 1), itl = split(l);
int res = 0;
for (auto it = itl; it != itr; it++)
res = (res + (it -> r - it -> l + 1) * qpow(it -> val, x, mod)) % mod;
return res;
}
int n, T, seed, mx, qwq;
int rnd()
{
int res = seed;
seed = (seed * 7 + 13) % (int)(1e9 + 7);
return res;
}
signed main()
{
#ifdef Fileopen
freopen(".in", r, stdin);
freopen(".out", w, stdout);
#endif
cin >> n >> T >> seed >> mx;
FOR(i, 1, n)Chtholly.insert(Node(i, i, (rnd() % mx) + 1));
while (T--)
{
int opt = (rnd() % 4) + 1, l = (rnd() % n) + 1, r = (rnd() % n) + 1, x, mod;
if (l > r)swap(l, r);
if (opt == 3)x = (rnd() % (r - l + 1)) + 1;
else x = (rnd() % mx) + 1;
if (opt == 4)mod = (rnd() % mx) + 1;
if (opt == 1)add(l, r, x);
else if (opt == 2)assign(l, r, x);
else if (opt == 3)cout << rk(l, r, x) << endl;
else cout << query(l, r, x, mod) << endl;
}
return 0;
}
2x02 例题二 CF915E##
2x02x01 分析##
操作 1 和 2 都为推平操作,设非工作日为 0, 工作日为 1,那么就与上面的推平操作一样了,注意此题有一点卡常
2x02x02 代码##
/*Code By Manipula*/
#include <bits/stdc++.h>
// #define Fileopen
// #pragma GCC optimize(2)
#define INF 0x3f3f3f3f
#define int long long
#define FOR(i, a, b) for (int i = a; i <= b; i++)
#define ROF(i, a, b) for (int i = a; i >= b; i--)
#define swap(x, y) x ^= y ^= x ^= y
#define sqrt(n) pow(n, 0.5)
#define sort stable_sort
#define mst(arr, num) memset(arr, num, sizeof(arr))
#define endl '\n'
using namespace std;
struct Node{
int l, r, val;
Node(int l, int r, int val) : l(l), r(r), val(val){}
bool operator < (const Node a) const{
return l < a.l;
}
};
set<Node> Chtholly;
auto split(int x)
{
auto it = Chtholly.lower_bound(Node{x, 0, 0});
if (it != Chtholly.end() && it -> l == x)return it;
it--;
if (it -> r < x)return Chtholly.end();
int l = it -> l, r = it -> r, val = it -> val;
Chtholly.erase(it);
Chtholly.insert(Node(l, x - 1, val));
return Chtholly.insert(Node{x, r, val}).first;
}
int n, T, sum;
void assign(int l, int r, int val)
{
auto itr = split(r + 1), itl = split(l);
for (auto it = itl; it != itr; it++)sum -= (it -> r - it -> l + 1) * it -> val;
Chtholly.erase(itl, itr);
Chtholly.insert(Node(l, r, val));
sum += val * (r - l + 1);
}
signed main()
{
#ifdef Fileopen
freopen(".in", r, stdin);
freopen(".out", w, stdout);
#endif
cin >> n >> T; sum = n;
Chtholly.insert(Node(1, n, 1));
while (T--)
{
int opt, l, r;
cin >> l >> r >> opt;
assign(l, r, opt - 1);
cout << sum << endl;
}
return 0;
}
3x00 总结##
珂朵莉树是骗分好手,有推平操作都可以达到骗分的目的,若数据随机甚至会 AC
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现