[OI] 珂朵莉树
对于一个序列,它有较多重复元素,并且题目需要维护区间修改,维护区间信息,维护整块值域信息的,那么就可以考虑珂朵莉树解决.
主要思想
珂朵莉树将全部相同的颜色块压缩为一组,如对于下述序列:
1 1 1 2 3 4 4 4 4
珂朵莉树铺平后即可以变为这样:
{1,3,1} {4,4,2} {5,5,3} {6,9,4}
其中的三元组,每一个三元组描述了一个区间,第一个数表示区间左端点,第二个数表示区间右端点,第三个点表示区间的值.
这样做可以降低区间操作的均摊复杂度,从而在部分数据下表现出较高的效率.
珂朵莉树内部使用 set 实现插入,查找与删除. 对于插入操作,一般来说区间修改操作复杂度是最优的,仅需要分裂并删除部分旁边区块,再整段插入即可,其他的插入操作思想与分块大同小异.
删除操作则是先寻找后分裂,并直接删除即可.
分裂操作的实质就是将几个段分成多个段,先删除原节点,再插入子区间节点.
主要操作
珂朵莉树的大部分操作与分块类似,只不过拥有特殊的块长和性质.
分裂
目标:分裂 \(x\) 所在的区间,返回左区间的首迭代器
这里要注意的是,如果你在其他操作中用到了分裂,一定要先获取右节点迭代器再获取左节点迭代器,否则可能会出现左区间在右区间修改时被修改,导致左迭代器失效 RE 的问题.
对于分裂函数内部,请保证在 Split 之前要有值,lower_bound 对空容器查找会 RE.
auto split(int x){
auto it=odt.lower_bound({x,0,0});
if(it!=odt.end() and it->l==x) return it;
it--;
node u=*it;
odt.erase(it);
odt.insert({u.l,x-1,u.v});
return odt.insert({x,u.r,u.v}).first;
}
铺平(区间修改)
void assign(int l,int r,int v){
auto itr=split(r+1),itl=split(l);
odt.erase(itl,itr);
odt.insert({l,r,v});
}
其他操作直接用迭代器暴力扫就行了,基本都一样,这里用查找排名来做例子.
查询排名
目标:查询 \([l,r]\) 内排名为 \(x\) 的数.
思路:爆扫,统计 \(cnt\)
int rank(int l,int r,int x){
auto itr=split(r+1),itl=split(l);
struct rank{
int v,cnt;
bool operator <(const rank &A)const{
return v<A.v;
}
};
vector<rank>v;
for(auto it=itl;it!=itr;++it){
v.push_back({it->v,it->r-it->l+1});
}
sort(v.begin(),v.end());
int i;for(i=0;i<=(int)v.size()-1;++i){
if(v[i].cnt<x){
x-=v[i].cnt;
}
else{
break;
}
}
return v[i].v;
}
CF896C Willem, Chtholly and Seniorious
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
int power(int n,int k,int p){
int ans=1,base=n;
while(k){
if(k&1){
ans=ans%p*base%p;
}
base=base%p*base%p;
k>>=1;
}
return ans;
}
class odt{
private:
struct node{
int l,r;
mutable int v;
bool operator <(const node &A)const{
return l<A.l;
}
};
set<node>odt;
public:
set<node>&self(){
return odt;
}
auto split(int x){
auto it=odt.lower_bound({x,0,0});
if(it!=odt.end() and it->l==x) return it;
it--;
node u=*it;
odt.erase(it);
odt.insert({u.l,x-1,u.v});
return odt.insert({x,u.r,u.v}).first;
}
void assign(int l,int r,int v){
auto itr=split(r+1),itl=split(l);
odt.erase(itl,itr);
odt.insert({l,r,v});
}
void add(int l,int r,int v){
auto itr=split(r+1),itl=split(l);
for(auto it=itl;it!=itr;++it){
it->v+=v;
}
}
int rank(int l,int r,int x){
auto itr=split(r+1),itl=split(l);
struct rank{
int v,cnt;
bool operator <(const rank &A)const{
return v<A.v;
}
};
vector<rank>v;
for(auto it=itl;it!=itr;++it){
v.push_back({it->v,it->r-it->l+1});
}
sort(v.begin(),v.end());
int i;for(i=0;i<=(int)v.size()-1;++i){
if(v[i].cnt<x){
x-=v[i].cnt;
}
else{
break;
}
}
return v[i].v;
}
int pow(int l,int r,int x,int y){
auto itr=split(r+1),itl=split(l);
int ans=0;
for(auto it=itl;it!=itr;++it){
ans=(ans+(it->r-it->l+1)%y*power(it->v,x,y))%y;
}
return ans;
}
};
odt tree;
int m,seed,vmax;
int Rand(){
int ret=seed;
seed=(seed*7+13)%1000000007;
return ret;
}
int a[100001];
signed main(){
cin>>n>>m>>seed>>vmax;
for(int i=1;i<=n;++i){
a[i]=Rand()%vmax+1;
tree.self().insert({i,i,a[i]});
}
for(int i=1;i<=m;++i){
int op=Rand()%4+1,l=Rand()%n+1,r=Rand()%n+1;
if(l>r) swap(l,r);
if(op==1){
int x=Rand()%vmax+1;
tree.add(l,r,x);
}
if(op==2){
int x=Rand()%vmax+1;
tree.assign(l,r,x);
}
if(op==3){
int x=Rand()%(r-l+1)+1;
cout<<tree.rank(l,r,x)<<endl;
}
if(op==4){
int x=Rand()%vmax+1,y=Rand()%vmax+1;
cout<<tree.pow(l,r,x,y)<<endl;
}
}
}
[OI] 珂朵莉树的更多相关文章
- 洛谷AT2342 Train Service Planning(思维,动态规划,珂朵莉树)
洛谷题目传送门 神仙思维题还是要写点东西才好. 建立数学模型 这种很抽象的东西没有式子描述一下显然是下不了手的. 因为任何位置都以\(k\)为周期,所以我们只用关心一个周期,也就是以下数都在膜\(k\ ...
- [转]我的数据结构不可能这么可爱!——珂朵莉树(ODT)详解
参考资料: Chtholly Tree (珂朵莉树) (应某毒瘤要求,删除链接,需要者自行去Bilibili搜索) 毒瘤数据结构之珂朵莉树 在全是珂学家的珂谷,你却不知道珂朵莉树?来跟诗乃一起学习珂朵 ...
- 洛谷P4344 [SHOI2015]脑洞治疗仪(珂朵莉树)
传送门 看到区间推倒……推平就想到珂朵莉树 挖脑洞直接assign,填坑先数一遍再assign再暴力填,数数的话暴力数 //minamoto #include<iostream> #inc ...
- 洛谷P2787 语文1(chin1)- 理理思维(珂朵莉树)
传送门 一看到区间推倒……推平操作就想到珂朵莉树 区间推平直接assign,查询暴力,排序的话开一个桶统计,然后一个字母一个字母加就好了 开桶统计的时候忘了保存原来的左指针然后挂了233 //mina ...
- 洛谷P2082 区间覆盖(加强版)(珂朵莉树)
传送门 虽然是黄题而且还是一波离散就能解决的东西 然而珂朵莉树还是很好用 相当于一开始区间全为0,然后每一次区间赋值,问最后总权值 珂朵莉树搞一搞就好了 //minamoto #include< ...
- 洛谷P2572 [SCOI2010]序列操作(珂朵莉树)
传送门 珂朵莉树是个吼东西啊 这题线段树代码4k起步……珂朵莉树只要2k…… 虽然因为这题数据不随机所以珂朵莉树的复杂度实际上是错的…… 然而能过就行对不对…… (不过要是到时候noip我还真不敢打… ...
- CF915E Physical Education Lessons(珂朵莉树)
中文题面 据说正解是动态开点线段树而且标记也不难下传的样子 然而这种区间推平的题目还是喜欢写珂朵莉树啊……码量小…… 虽然真要构造的话随便卡…… //minamoto #include<cstd ...
- CF896C Willem, Chtholly and Seniorious(珂朵莉树)
中文题面 珂朵莉树的板子……这篇文章很不错 据说还有奈芙莲树和瑟尼欧里斯树…… 等联赛考完去学一下(逃 //minamoto #include<bits/stdc++.h> #define ...
- 珂朵莉树(Chtholly Tree)学习笔记
珂朵莉树(Chtholly Tree)学习笔记 珂朵莉树原理 其原理在于运用一颗树(set,treap,splay......)其中要求所有元素有序,并且支持基本的操作(删除,添加,查找......) ...
- LOJ#557. 「Antileaf's Round」你这衣服租来的吗(FHQ Treap+珂朵莉树)
题面 传送门 题解 好吧我是不太会复杂度分析-- 我们对于每种颜色用一个数据结构维护(比方说线段树或者平衡树,代码里写的平衡树),那么区间询问很容易就可以解决了 所以现在的问题是区间修改,如果区间颜色 ...
随机推荐
- windows上传app到构建版本的方法
ios打包好ipa文件后,ipa文件需要上架到app store,用户才能安装.而在app store里,无法直接将ipa上传,需要使用工具上传,但是官方提供的工具,比如xcode等只能安装在苹果电脑 ...
- Jmeter函数助手40-escapeHtml
escapeHtml函数用于将字符串进行HTML转义. 字符串转义:填入字符串 1.escapeHtml函数是将字符换进行HTML转义,jmeter还存在其它格式化函数如urlencode函数,url ...
- 【C3】05 层叠与继承
本文旨在让你理解CSS的一些最基本的概念 --层叠.优先级和继承-- 这些概念决定着如何将CSS应用到HTML中,以及如何解决冲突. 尽管与课程的其他部分相比,完成这节课可能看起来没有那么直接的相关性 ...
- 【DataBase】MySQL 07 SQL函数 单行函数其一 字符函数
SQL函数的概念 -- SQL函数 -- 概念:类似Java的方法,将已经定义好的不再改变的逻辑语句封装在函数体内,对外提供方法的标识 -- 隐藏了实现细节,提高功能的可重用 -- SELECT 函数 ...
- faster-fifo:C++实现的python多进程通信队列 —— 强化学习ppo算法库sample-factory的C++实现的python多进程通信队列 —— python3.12版本下成功通过测试
项目地址: https://github.com/alex-petrenko/faster-fifo 需要注意,该项目给出了两种安装方法,一种是pip从pypi官网安装,一种是从GitHub上的源码安 ...
- vscode设置字体大小
1.背景 2.设置编辑器字体大小 3.设置窗口字体大小 完美!
- Apache DolphinScheduler 在奇富科技的首个调度异地部署实践
奇富科技(原360数科)是人工智能驱动的信贷科技服务平台,致力于凭借智能服务.AI研究及应用.安全科技,赋能金融机构提质增效,助推普惠金融高质量发展,让更多人享受到安全便捷的金融科技服务.作为国内领先 ...
- Headless靶机笔记
Headless靶机 靶机概述 Headless 是一款简单易难的 Linux 机器,具有python实现的托管网站的服务器.基本思路: 通过端口探测到web页面,有一个表单. 利用忙注XSS,获得管 ...
- YOLOv10添加输出各类别训练过程指标
昨天有群友,在交流群[群号:392784757]里提到了这个需求,进行实现一下 V10 官方代码结构相较于 V8 稍微复杂一些 yolov10 是基于 v8 的代码完成开发,yolov10 进行了继承 ...
- Win32封装对话框类
[主程序入口.cpp] #include <windows.h> #include <tchar.h> #include "resource.h" #inc ...