以下内容基于 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;
}

基于链表实现的珂朵莉树相关推荐

  1. 【日志】珂学——珂朵莉树

    珂朵莉树 (珂学) 珂朵莉树(或者老司机树)起源于CF896C. 由于之前做到每一组数据都要另外开数据结构,所以现在一些东西就会写为class包装 前置知识点 STL中set的使用(list也行,但是 ...

  2. [python刷题模板] 珂朵莉树 ODT (基于支持随机访问的跳表

    [python刷题模板] 珂朵莉树 ODT (基于支持随机访问的跳表) 一. 算法&数据结构 1. 描述 2. 复杂度分析 3. 常见应用 4. 常用优化 二. 模板代码 0. 区间推平(lg ...

  3. [转]我的数据结构不可能这么可爱!——珂朵莉树(ODT)详解

    参考资料: Chtholly Tree (珂朵莉树) (应某毒瘤要求,删除链接,需要者自行去Bilibili搜索) 毒瘤数据结构之珂朵莉树 在全是珂学家的珂谷,你却不知道珂朵莉树?来跟诗乃一起学习珂朵 ...

  4. 珂朵莉树/ODT 学习笔记

    珂朵莉树/ODT 学习笔记 起源自 CF896C.珂朵莉yyds! 核心思想 把值相同的区间合并成一个结点保存在 set 里面. 用处 骗分.只要是有区间赋值操作的数据结构题都可以用来骗分.在数据随机 ...

  5. codeforces 915 E 896 C 珂朵莉树

    珂朵莉树(Chtholly Tree),一种基于std::set的暴力数据结构,是由某毒瘤在2017年的一场CF比赛中提出的数据结构,原名老司机树(Old Driver Tree,ODT).由于第一个 ...

  6. 我的算法不可能这么简单—珂朵莉树

    文章目录 进入正题 珂朵莉树的起源 题目简述 题目分析 珂朵莉树的实现 萌新三连 1.明明查询的右端点是12,但是要split(13)呢? 2.为什么要先分裂右端点,然后再分裂左端点呢? 3.获取到区 ...

  7. [python刷题模板] 珂朵莉树 ODT(20220928弃用,请看新文)

    [python刷题模板] 珂朵莉树 ODT (基于SortedList 20220928弃用,请看新文) 一. 算法&数据结构 1. 描述 2. 复杂度分析 3. 常见应用 4. 常用优化 二 ...

  8. 珂朵莉树(odt老司机树)

    传送门 题意:对于一段区间一共有四种操作: 题解:珂朵莉树板题 珂朵莉树,又称Old Driver Tree(ODT).是一种基于std::set的暴力数据结构. 关键操作:推平一段区间,使一整段区间 ...

  9. CodeForces - 897E Willem, Chtholly and Seniorious(珂朵莉树)

    题目链接:点击查看 题目大意:给出一个长度为 nnn 的数列,现在需要执行 mmm 次操作,每次操作分为下列四种情况: 1lrx1 \ l \ r \ x1 l r x:[l,r][l,r][l,r] ...

最新文章

  1. 机器学习数据管理初创公司SafeGraph融资1600万美元
  2. python 从网络URL读取图片并直接处理的代码
  3. 目标检测优化2021
  4. 第四章 对象的类型和动态绑定
  5. unlink与close关系
  6. MySQL MyISAM和InnoDB存储引擎的比较
  7. iOS之深入解析谓词NSPredicate的语法与应用
  8. python3之后版本读取网页的内容
  9. bzoj1965 [AHOI2005]洗牌 结论
  10. 紫光展锐回应“春藤510只支持NSA”:错误解读 SA和NSA一个都不少
  11. Unity3D 笔试题
  12. 力控组态软件 mysql_组态软件国内那家做的好?推荐几个比较一下
  13. qq连连看java版_Java实战_仿QQ连连看
  14. Linux-you need at least 8.6GB disk space to install Ubuntu,this computer has only 8GB
  15. 2022年物理学诺奖获主,他们证明爱因斯坦错了
  16. 双向链表(double linked list)
  17. 软件工程各个流程主要的图
  18. 游戏服务器生成全局唯一ID的几种方法
  19. Linux ns 5. IPC Namespace 详解
  20. toooomuch和toooomuch2的wp

热门文章

  1. 第三章:zigbee学习笔记之物理层和mac层帧格式分析
  2. C 用单向链表建立一张班级成绩单,包括每个学生的学号、姓名、英语、高等数学、普通物理、C语言程序设计4门课程的成绩。实现以下功能,并提供菜单选项:(只实现了部分功能)
  3. c语言删除数组中的元素
  4. 【数据库专题】耀杨:听说DDL想c我?——《狗叫江湖》“第四幕”
  5. oracle联合主键删除,oracle数据库删除联合主键
  6. web前端换行代码的几种实现方式!
  7. 为什么数据库字段要使用NOT NULL?
  8. CSS absolute、fixed、relative、static等定位 笔记
  9. IMX6ULL学习笔记(9)——通过SD卡启动Linux内核
  10. 电机学习笔记 h桥与自举电路