好Van的珂朵莉树

珂朵莉树

珂朵莉树的主要操作是区间覆盖,即将区间\([l,r]\)全部染色为\(c\)。

EXAMPLE

EXAMPLE 1

给出一个长度为\(n\)的序列,一共\(q\)次询问,每次询问给出\(m\)个区间,求这些区间并集的权值和。

\(n \leq 10^5,\sum m \leq 10^5\)

SOLUTION 1

显然能用珂朵莉树做

珂朵莉树是一种基于std::set的暴力数据结构。

这个set维护若干区间,这些区间没有交集,且按左端点从小到大有序。

struct node {
    int l, r, c;
    int operator<(const node& a) const { return l < a.l; }
};
set<node> S;
typedef set<node>::iterator IT;

珂朵莉树的基本操作有两个:

  • split(pos)

这个操作把包含了\(pos\)位置的区间分成两段,分别为\([l,pos-1]\)和\([pos,r]\),且返回\([pos,r]\)的指针。

写法也很简单:

IT split(int po) {
    IT it = S.lower_bound((node){po, -1, 0});
    if (it != S.end() && it->l == po) return it;
    --it;
    int l = it->l, r = it->r, c = it->c;
    S.erase(it);
    S.insert((node){l, po - 1, c});
    return S.insert((node){po, r, c}).first;
}
  • assign(l, r, c)

这个操作将被\([l,r]\)包含的区间全部删除,然后用颜色\(c\)覆盖区间\([l,r]\)。

代码:

void cover(int l, int r, int c) {
    IT itr = split(r + 1), itl = split(l);
    S.erase(itl, itr); //意思是删除[itl, itr),注意左闭右开
    S.insert((node){l, r, c});
    reset(c, sum[r] - sum[l - 1]);
}


那么,对于上面那题,怎么用珂朵莉树解决?

对于每个询问,我们直接cover(l, r),由于操作的是整个区间,答案的增减可以直接\(O(1)\)得到,代码只需要在上面的写法上变一下即可:

void cover(int l, int r, int c) {
    IT itr = split(r + 1), itl = split(l), it = itl;
    for (; it != itr; ++it) {
        //这里可以直接根据it的左右端点增减答案
    }
    S.erase(itl, itr);
    S.insert((node){l, r, c});
    reset(c, sum[r] - sum[l - 1]);
}

EXAMPLE 2

给出一个\(n\)个节点的树以及\(m\)条树上路径,共\(q\)个询问,每次询问\([l_i,r_i]\)这些路径的并的权值。

SOLUTION 2

先离线询问,把询问\([l,r]\)挂在\(r\)。从左往右扫时,我们把第\(i\)条路径上的每个点染色为\(i\),然后遍历挂在\(i\)上的询问,设其左端点是\(l\),那么颜色\(>=l\)的点都有贡献。

树链剖分后路径染色转化为区间问题,为了统计\(c>=l\)的贡献和,我们再使用树状数组维护,用珂朵莉树解决染色问题,这题就搞定了。

复杂度

如果被覆盖的区间毫无贡献,那么可以证明珂朵莉树复杂度是\(O(nlogn)\)的。

因为每次assign最多增加\(3\)个区间,每次增加是\(O(logn)\)的,删除区间是均摊\(O(logn)\)的,所以复杂度是\(O(nlogn)\)的。



又扩展技能树啦~

原文地址:https://www.cnblogs.com/zjlcnblogs/p/11566816.html

时间: 2024-11-05 21:48:36

好Van的珂朵莉树的相关文章

[SHOI2015]脑洞治疗仪(线段树?珂朵莉树)

题面 这道题超级可爱呢,珂朵莉最可爱了,不,小哀才是最可爱的呢 很好的题,可以考虑用线段树维护,hale表示线段树思路很难,而且难打,不如滚去写珂朵莉树哦 对于操作一:直接将set修改插入即可 对于操作三:最大连续子段和(线段树里面是这样叫的吧)维护即可 对于操作二:我们发现可以考虑先将这段区间里面的1 全部取出来,然后暴力合并区间为0,插入会set里面 之后枚举要修改的区间,从左端点开始搞起,一直后搜索,最后加一个判断,是否已经完全ok即可,具体可参见代码 好了,这道题就解决了 我的代码好像l

P2787 语文1(chin1)- 理理思维(珂朵莉树)

珂朵莉树模板,区间排序就暴力地取二十六个字母出来并且计数,然后重新从小到大插入即可 代码: #include <bits/stdc++.h> #define int long long #define sc(a) scanf("%lld",&a) #define scc(a,b) scanf("%lld %lld",&a,&b) #define sccc(a,b,c) scanf("%lld %lld %lld"

模板—珂朵莉树

其实本质上是优化暴力. 网上都说构造的数据可以卡掉珂朵莉树,是因为在修改的时候要遍历set导致很容易卡掉,所以珂朵莉树可能比较有局限性. 但是如果用来维护区间用于求交求并,复杂度是严格的log的,常数好像稍大,但是还是非常有用的. 放个板子: 1 #include<iostream> 2 #include<cstdio> 3 #include<set> 4 #define re register 5 #define co const 6 #define cor co r

CF896C Willem, Chtholly and Seniorious 珂朵莉树

问题描述 CF896C LG-CF896C 题解 我expect就是T飞,从这里跳下去,也不碰和珂朵莉相关的任何东西. 珂朵莉树真好使. 珂朵莉树模板. \(\mathrm{Code}\) #include<bits/stdc++.h> using namespace std; #define int long long #define IT set<node>::iterator template <typename Tp> void read(Tp &x){

LG4979 矿洞:坍塌 珂朵莉树

问题描述 LG4979 题解 珂朵莉树+O2简直就是绝配 对于操作 A ,直接 \(\mathrm{assign}\) 推平就完事了. 对于操作 B ,如果它左右端点有在边界上的,直接把区间 \([l,r]\)撕出来判断就完了,如果不在边界上,先把单点 \({l-1,r+1}\) 撕出来判,如果符合条件,再撕 \([l,r]\) 出来判. \(\mathrm{Code}\) #include<bits/stdc++.h> using namespace std; #define IT set&

Solution: 题解 CF896C Willem, Chtholly and Seniorious(线段树解珂朵莉树)

Intro: 珂朵莉树模板题 怎么所有题解都是珂朵莉树啊啊啊啊 于是本蒟蒻决定来一发中(feng)规(kuang)中(luan)矩(gao)的线段树 首先这棵线段树只维护懒标记 来一发定义 线段树节点\(u\)维护区间\([l_u,r_u]\)的内容 懒标记\(t_u\):当\(t_u\not=0\)时表示区间\([l_u,r_u]\)全是\(t_u\),\(t_u=0\)就是没有懒标记 建立线段树 在建立时顺便处理\(l_u,r_u\),只要当\(l_u=r_u\)时就打上标记 P.s \(L

ODT (Old Driver Tree)珂朵莉树

1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 const int MOD7 = 1e9 + 7; 6 const int MOD9 = 1e9 + 9; 7 const int imax_n = 1e5 + 7; 8 LL add(LL a, LL b, LL mod) 9 { 10 LL res = 0; 11 LL ans = a; 12 while (b) 13 { 14 if

珂朵莉树

将一段区间的值全变成c 区间加 区间第k小的数 区间幂次和 传送门 模板 #include <iostream> #include <cstdio> #include <cstring> #include <vector> #include <set> #include <algorithm> #define IT set<node>::iterator using namespace std; const int max

[Codeforces896C] Willem, Chtholly and Seniorious (ODT-珂朵莉树)

无聊学了一下珂朵莉树 珂朵莉树好哇,是可以维护区间x次方和查询的高效数据结构. 思想大致就是一个暴力(相对而言)的树形数据结构 lxl毒瘤太强了,发明了ODT算法(Old Driver Tree老司机算法) 这里有一个大佬ACDreamer的题解 附上链接https://www.luogu.org/blog/ACdreamer/solution-cf896c 还有一个B站的讲解视频 附上链接https://www.bilibili.com/video/av21651173 我不会用珂朵莉树,但是