首页 > 其他分享 >【学习笔记】珂朵莉树

【学习笔记】珂朵莉树

时间:2024-08-18 16:05:38浏览次数:6  
标签:学习 set int pos 笔记 朵莉树 split 区间 col

前言

  • 珂朵莉树(Chtholly Tree),又名老司机树(Old Driver Tree),起源自 CF896C Willem, Chtholly and Seniorious
  • 严格来说,珂朵莉树这种想法是基于数据随机的颜色段均摊,而不是一种数据结构,可作为一些题目的暴力做法(因此原题被分到了暴力数据结构的标签),在随机数据下一般效率较高。

基础知识

  • 珂朵莉树常用来解决区间推平操作,核心思想是将值相同的区间合并成一个节点并保存到 set 里。
  • 修改时再将区间分裂开进行操作。
  • 随机数据下区间加、区间推平、区间求和用 set 实现的珂朵莉树的时间复杂度为 \(O(n \log \log n)\) ,而用链表实现的珂朵莉树的时间复杂度为 \(O(n \log n)\) 。证明建议参考 Codeforces 上关于珂朵莉树的复杂度的证明 | 珂朵莉树的复杂度分析

代码实现

  • 保存节点
    • 定义一个结构体 node 记录每个值 \(col\) 相同的一段区间 \([l,r]\) 。

      点击查看代码
      struct node
      {
      	int l,r;
      	mutable int col;
      	bool operator < (const node &another) const
      	{
      		return l<another.l;
      	}
      };
      set<node>s; 
      
    • 为方便随时修改已经插入到 set 里的元素的 \(col\) 而不用将这个元素先取出再重新加入 set ,我们让 \(col\) 被 C++ 关键字 mutable 修饰,使其处于可变状态。

      • mutable 只能用于修饰类中的非静态数据成员。
  • 初始化 init
    • 不做讲解。

      点击查看代码
      void init(int n,int a[])
      {
      	s.clear();
      	for(int i=1;i<=n;i++)
      	{
      		s.insert((node){i,i,a[i]});
      	}
      }
      
  • 分裂 split
    • 将原先包含 \(pos\) 的区间 \([l,r]\) 分裂成两部分 \([l,pos-1],[pos,r]\) 并返回后者的迭代器。

      点击查看代码
      set<node>::iterator split(int pos)
      {
      	set<node>::iterator it=s.lower_bound((node){pos,0,0});
      	if(it!=s.end()&&it->l==pos)
      	{
      		return it;
      	}
      	it--;
      	if(it->r<pos)
      	{
      		return s.end();
      	}
      	int l=it->l,r=it->r,col=it->col;
      	s.erase(it);
      	s.insert((node){l,pos-1,col});
      	return s.insert((node){pos,r,col}).first;
      }
      
    • 假设我们当前找到了 \(it\) 这个位置,有三种情况。

      • 第一,这个区间以 \(pos\) 开头,直接返回 \(it\) 即可。
      • 第二,找到的区间比正好包含 \(pos\) 的区间大一点点;第三, \(pos\) 太大了,超过了最后一个区间的右端点。不妨先把 \(it\) 向前挪一个位置,判断 \(it\) 的右端点是否比 \(pos\) 小,若成立说明与第三种情况对应,返回 \(s\) 的尾迭代器,否则说明 \(it\) 包含了包含 \(pos\) 的区间,一分为二后删除原区间 \(it\) 并插入两个新区间 \([l,pos-1],[pos,r]\) 。
    • 又因为 .insert() 返回值的第一关键字就是插入位置的迭代器,直接返回即可。

      • 关于 .insert() 返回值的详细信息建议参考 cppreference
    • 接下来对于 \([l,r]\) 的区间操作都能转化到 set 上 \([split(l),split(r+1)-1]\) 的操作。

  • 推平 assign
    • 把整个 set 中需要合并的区间全部删除再重新插入一个区间即可。

    • 使用 .erase(first,last) 来辅助我们进行删除(区间为左闭右开 \([first,last)\) )。

      点击查看代码
      void assign(int l,int r,int col)
      {
      	set<node>::iterator itr=split(r+1),itl=split(l);
      	s.erase(itl,itr);
      	s.insert((node){l,r,col});
      }
      
    • 为避免因 .erase() 导致的迭代器失效问题,先执行 split(r+1) 再执行 split(l)

      • 若顺序颠倒可能导致 split(l) 得到的迭代器在 split(r+1)erase 过程中时效从而导致 \(RE\) 。
    • 其他操作同理。

  • 修改 update / 询问 query
    • 以区间加、区间求和为例。
      点击查看代码 ```cpp void update(int l,int r,int val) { set::iterator itr=split(r+1),itl=split(l); for(set::iterator it=itl;it!=itr;it++) { it->col+=val; } } int query(int l,int r) { set::iterator itr=split(r+1),itl=split(l); int ans=0; for(set::iterator it=itl;it!=itr;it++) { ans+=it->col*(it->r-it->l+1); } return ans; } ```

例题

SP13015 CNTPRIME - Counting Primes

SP19568 PRMQUER - Prime queries

CF915E Physical Education Lessons

CF896C Willem, Chtholly and Seniorious

CF1638E Colorful Operations

luogu P4690 [Ynoi2016] 镜中的昆虫

参考文章

标签:学习,set,int,pos,笔记,朵莉树,split,区间,col
From: https://www.cnblogs.com/The-Shadow-Dragon/p/18365412

相关文章

  • python入门机器学习4:pandas入门
     一.Series:一维数组,listimportnumpyasnpimportpandasaspdmyarray=np.array([1,2,3])myindex=['a','b','c']myseries=pd.Series(myarray,index=myindex)print(myseries)print(myseries[0])#第一个元素print(myseries['c'])#in......
  • MIRec论文阅读笔记
    MIRec:NeuralNewsRecommendationwithMulti-InterestandPopularity-AwareModeling论文阅读笔记Abstract现存的问题:​ 现有方法主要是为每个用户学习一个统一的嵌入向量来代表其兴趣。然而,由于缺乏表现力,单一的嵌入表示法无法充分表达用户的不同兴趣。此外,将新闻流行度纳......
  • 物理 选择性必修一 第三章 学习笔记
    1.波的形成振动的传播称为波动,简称波(wave)。我们把物体或物体的一部分在一个位置附近的往复运动称为机械振动(mechanicalvibration),简称振动。(X1-2)以抖动绳子为例,绳子是有弹性的物体,设想把一条绳子分成一个个小段,这些小段可以看作一个个相连的质点。当手握绳端上下振动时,绳端带......
  • 计算机毕业设计选题推荐-在线学习平台-Java/Python项目实战
    ✨作者主页:IT研究室✨个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。☑文末获取源码☑精彩专栏推荐⬇⬇⬇Java项目Python项目安卓项目微信小程序项目......
  • Markdown学习
    标题这是一个二级标题##这是一个二级标题这是一个三级标题###这是一个三级标题字体这是一段普通文本这是一段粗体文本**这是一段粗体文本**这是一段斜体文本*这是一段斜体文本*这是一段粗体且斜体文本***这是一段粗体且斜体文本***这段文本应用了删除线~~这......
  • 拟剧论笔记
    目录拟剧论笔记理想化理想化呈现积极理想化消极理想化不恰当享乐不呈现社会手段的失序理想资格的确立拟剧论笔记理想化理想化呈现积极理想化呈现出一种比自己真实状态更好的身份特质。呈现策略是表演一种比自己更好的状态,从而得到更多的认同。这是一种由表及里的自己呈现的......
  • FreeRTOS学习:任务调度
     注:在使用大多数功能时,FreeRTOS都要将对应的宏置为1,具体的需要查看FreeRTOS官方文档。 任务堆栈相关寄存器如下,启动第一个任务FreeRTOS中启动第一个任务的流程总结如下,启动任务调度器vTaskStartScheduler()在该函数中会创建空闲任务prvIdleTask和软件定时器任务xTimerC......
  • 零基础学习人工智能—Python—Pytorch学习(五)
    前言上文有一些文字打错了,已经进行了修正。本文主要介绍训练模型和使用模型预测数据,本文使用了一些numpy与tensor的转换,忘记的可以第二课的基础一起看。线性回归模型训练结合numpy使用首先使用datasets做一个数据X和y,然后结合之前的内容,求出y_predicted。#pipinstallmatp......
  • DP学习笔记
    动态规划算法与分治法类似,是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始......
  • tarjan之LCA学习笔记
    tarjan之LCA学习笔记tarjan算法求LCA可谓是一个极其巧妙的离线算法其本质是利用DFS遍历时产生的DFS序和并查集来在线性的时间复杂度内求出所有询问的结果既然是离线算法,其和在线算法的区别就在与离线算法需要记录下所有查询,对查询进行一定操作来得到更高的效率,而这......