基于链表实现的珂朵莉树
以下内容基于 CF896C Willem, Chtholly and Seniorious 来介绍。
珂朵莉树实质上是一种可以维护区间上的分裂与合并的数据结构,但要求数据是随机的,或者有大量的随机合并操作,这样才能保证维护的区间个数是一个很小的值。
一开始,我们用不同的节点表示 [1,1],[2,2],...,[n,n][1,1],[2,2],...,[n,n][1,1],[2,2],...,[n,n] 以及该区间上的值。
本题中的“把区间 [l,r][l,r][l,r] 赋值为 xxx”对应着一个合并操作,若随机到的 [l,r][l,r][l,r] 范围比较大,则意味着有大量的节点会合并成一个节点。经测试,在若干次随机合并后,区间个数会骤降至一个稳定的范围(大约几十个),这是理解珂朵莉树的关键。
图例:横轴为操作次数,纵轴为区间个数
数据定义
目前主流的实现是基于 set
来维护节点,但由于平均维护的区间个数很小,set
的优势并不明显。相比之下,链表(或数组)能更简洁地维护分裂与合并操作。
typedef long long int64;struct Block {Block *next; // 链表下一节点int l, r; // 区间范围int64 val; // 区间上的值Block(Block *next, int l, int r, int64 val): next(next), l(l), r(r), val(val) {}bool operator<(const Block &b) const { return val < b.val; }
} *root;
基本操作
分裂区间
void split(int mid) {// 遍历链表for (Block *b = root; b; b = b->next) {// 寻找能包含 mid 和 mid+1 的 [l, r],将其被拆分成 [l, mid] 和 [mid+1, r]if (b->l <= mid && mid + 1 <= b->r) { b->next = new Block(b->next, mid + 1, b->r, b->val);b->r = mid;break;}}
}
在操作区间时,由于不能只维护区间的一部分,所以下面的操作进行之前都需要预先分裂区间,再完成相应操作。
// 预分裂,保证后续操作在 [l, r] 内部
void prepare(int l, int r) {split(l - 1);split(r);
}
合并区间
void merge(int l, int r, int64 val) {prepare(l, r)// 遍历链表for (Block *b = root; b; b = b->next) {// 寻找区间左端点if (b->l == l) {// 将区间 [b.l, b.r] 修改成 [b.l, r]b->r = r;b->val = val;// 然后寻找与 [b.l, r] 右侧相邻的区间,将当前节点链至该区间Block *tmp = b->next;while (tmp && tmp->l <= r) tmp = tmp->next;b->next = tmp;break;}}
}
// 注:这里没有释放被删除节点的内存,若有需要可自行添加
区间修改与计算
由于数据量很小,枚举整个链表即可。
// 区间更新
void add(int l, int r, int64 val) {prepare(l, r)for (Block *b = root; b; b = b->next) {if (l <= b->l && b->r <= r) b->val += val;}
}// 区间第 k 小
int64 kth(int l, int r, int k) {prepare(l, r)vector<Block> blocks;for (Block *b = root; b; b = b->next) {if (l <= b->l && b->r <= r) {blocks.emplace_back(*b);}}sort(blocks.begin(), blocks.end());k--;for (Block b: blocks) {int cnt = b.r - b.l + 1;if (k >= cnt) k -= cnt;else return b.val;}
}// 快速幂
int64 quick_pow(int64 x, int n, int64 mod) {x %= mod;int64 res = 1 % mod;for (; n; n >>= 1) {if (n & 1) res = res * x % mod;x = x * x % mod;}return res;
}// 区间幂和
int64 pow_sum(int l, int r, int n, int64 mod) {prepare(l, r)int64 sum = 0;for (Block *b = root; b; b = b->next) {if (l <= b->l && b->r <= r) {sum += int64(b->r - b->l + 1) * quick_pow(b->val, n, mod);}}return sum % mod;
}
AC 代码
#include <iostream>
#include <vector>
#include <algorithm>using namespace std;
typedef long long int64;int64 seed;// 生成 [0, n-1] 的随机数
int rand(int n) {int64 ret = seed;seed = (seed * 7 + 13) % int64(1e9 + 7);return int(ret) % n;
}struct Block {Block *next;int l, r;int64 val;Block(Block *next, int l, int r, int64 val): next(next), l(l), r(r), val(val) {}bool operator<(const Block &b) const { return val < b.val; }
} *root;void split(int mid) {for (Block *b = root; b; b = b->next) {if (b->l <= mid && mid + 1 <= b->r) {b->next = new Block(b->next, mid + 1, b->r, b->val);b->r = mid;break;}}
}void prepare(int l, int r) {split(l - 1);split(r);
}void add(int l, int r, int64 val) {for (Block *b = root; b; b = b->next) {if (l <= b->l && b->r <= r) b->val += val;}
}void merge(int l, int r, int64 val) {for (Block *b = root; b; b = b->next) {if (b->l == l) {b->r = r;b->val = val;Block *tmp = b->next;while (tmp && tmp->l <= r) tmp = tmp->next;b->next = tmp;break;}}
}int64 kth(int l, int r, int k) {vector<Block> blocks;for (Block *b = root; b; b = b->next) {if (l <= b->l && b->r <= r) {blocks.emplace_back(*b);}}sort(blocks.begin(), blocks.end());k--;for (Block b: blocks) {int cnt = b.r - b.l + 1;if (k >= cnt) k -= cnt;else return b.val;}
}int64 quick_pow(int64 x, int n, int64 mod) {x %= mod;int64 res = 1 % mod;for (; n; n >>= 1) {if (n & 1) res = res * x % mod;x = x * x % mod;}return res;
}int64 pow_sum(int l, int r, int n, int64 mod) {int64 sum = 0;for (Block *b = root; b; b = b->next) {if (l <= b->l && b->r <= r) {sum += int64(b->r - b->l + 1) * quick_pow(b->val, n, mod);}}return sum % mod;
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int n, m, v_max;cin >> n >> m >> seed >> v_max;root = new Block(nullptr, 0, 0, rand(v_max) + 1);Block *b = root;for (int i = 1; i < n; i++) {b->next = new Block(nullptr, i, i, rand(v_max) + 1);b = b->next;}for (; m; m--) {int op = rand(4) + 1;int l = rand(n), r = rand(n);if (l > r) swap(l, r);int x = op == 3 ? rand(r - l + 1) + 1 : rand(v_max) + 1;prepare(l, r); // 在所有操作前预分裂switch (op) {case 1:add(l, r, x);break;case 2:merge(l, r, x);break;case 3:cout << kth(l, r, x) << '\n';break;default:int64 y = rand(v_max) + 1;cout << pow_sum(l, r, x, y) << '\n';break;}}return 0;
}
基于链表实现的珂朵莉树相关推荐
- 【日志】珂学——珂朵莉树
珂朵莉树 (珂学) 珂朵莉树(或者老司机树)起源于CF896C. 由于之前做到每一组数据都要另外开数据结构,所以现在一些东西就会写为class包装 前置知识点 STL中set的使用(list也行,但是 ...
- [python刷题模板] 珂朵莉树 ODT (基于支持随机访问的跳表
[python刷题模板] 珂朵莉树 ODT (基于支持随机访问的跳表) 一. 算法&数据结构 1. 描述 2. 复杂度分析 3. 常见应用 4. 常用优化 二. 模板代码 0. 区间推平(lg ...
- [转]我的数据结构不可能这么可爱!——珂朵莉树(ODT)详解
参考资料: Chtholly Tree (珂朵莉树) (应某毒瘤要求,删除链接,需要者自行去Bilibili搜索) 毒瘤数据结构之珂朵莉树 在全是珂学家的珂谷,你却不知道珂朵莉树?来跟诗乃一起学习珂朵 ...
- 珂朵莉树/ODT 学习笔记
珂朵莉树/ODT 学习笔记 起源自 CF896C.珂朵莉yyds! 核心思想 把值相同的区间合并成一个结点保存在 set 里面. 用处 骗分.只要是有区间赋值操作的数据结构题都可以用来骗分.在数据随机 ...
- codeforces 915 E 896 C 珂朵莉树
珂朵莉树(Chtholly Tree),一种基于std::set的暴力数据结构,是由某毒瘤在2017年的一场CF比赛中提出的数据结构,原名老司机树(Old Driver Tree,ODT).由于第一个 ...
- 我的算法不可能这么简单—珂朵莉树
文章目录 进入正题 珂朵莉树的起源 题目简述 题目分析 珂朵莉树的实现 萌新三连 1.明明查询的右端点是12,但是要split(13)呢? 2.为什么要先分裂右端点,然后再分裂左端点呢? 3.获取到区 ...
- [python刷题模板] 珂朵莉树 ODT(20220928弃用,请看新文)
[python刷题模板] 珂朵莉树 ODT (基于SortedList 20220928弃用,请看新文) 一. 算法&数据结构 1. 描述 2. 复杂度分析 3. 常见应用 4. 常用优化 二 ...
- 珂朵莉树(odt老司机树)
传送门 题意:对于一段区间一共有四种操作: 题解:珂朵莉树板题 珂朵莉树,又称Old Driver Tree(ODT).是一种基于std::set的暴力数据结构. 关键操作:推平一段区间,使一整段区间 ...
- CodeForces - 897E Willem, Chtholly and Seniorious(珂朵莉树)
题目链接:点击查看 题目大意:给出一个长度为 nnn 的数列,现在需要执行 mmm 次操作,每次操作分为下列四种情况: 1lrx1 \ l \ r \ x1 l r x:[l,r][l,r][l,r] ...
最新文章
- 机器学习数据管理初创公司SafeGraph融资1600万美元
- python 从网络URL读取图片并直接处理的代码
- 目标检测优化2021
- 第四章 对象的类型和动态绑定
- unlink与close关系
- MySQL MyISAM和InnoDB存储引擎的比较
- iOS之深入解析谓词NSPredicate的语法与应用
- python3之后版本读取网页的内容
- bzoj1965 [AHOI2005]洗牌 结论
- 紫光展锐回应“春藤510只支持NSA”:错误解读 SA和NSA一个都不少
- Unity3D 笔试题
- 力控组态软件 mysql_组态软件国内那家做的好?推荐几个比较一下
- qq连连看java版_Java实战_仿QQ连连看
- Linux-you need at least 8.6GB disk space to install Ubuntu,this computer has only 8GB
- 2022年物理学诺奖获主,他们证明爱因斯坦错了
- 双向链表(double linked list)
- 软件工程各个流程主要的图
- 游戏服务器生成全局唯一ID的几种方法
- Linux ns 5. IPC Namespace 详解
- toooomuch和toooomuch2的wp
热门文章
- 第三章:zigbee学习笔记之物理层和mac层帧格式分析
- C 用单向链表建立一张班级成绩单,包括每个学生的学号、姓名、英语、高等数学、普通物理、C语言程序设计4门课程的成绩。实现以下功能,并提供菜单选项:(只实现了部分功能)
- c语言删除数组中的元素
- 【数据库专题】耀杨:听说DDL想c我?——《狗叫江湖》“第四幕”
- oracle联合主键删除,oracle数据库删除联合主键
- web前端换行代码的几种实现方式!
- 为什么数据库字段要使用NOT NULL?
- CSS absolute、fixed、relative、static等定位 笔记
- IMX6ULL学习笔记(9)——通过SD卡启动Linux内核
- 电机学习笔记 h桥与自举电路