珂朵莉树

老师给的数据结构题单里出现的,会的同学教了一下。没有想象中的难。

思路

基础的珂朵莉树其实就是用 set 维护一个数组上的连续相同区间,每一个区间用一个结构体表示。

struct node
{
    int l, r; //区间左右端点
    mutable int v; //区间里每个数的值
    friend bool operator < (node x, node y)
    {
        return x.l < y.l;
    } //从左往右排好序
};
typedef set <node> :: iterator snodeit;
set <node> s;

基础操作

split

要处理某一个点 \(x\) 时,可以把这个点所在的区间拆成 \([l,x)\)\([x,r]\) 并返回指向后者的迭代器,过程如下。

snodeit split(int pos)
{
    snodeit it = s.lower_bound ({pos, 0, 0});//找到区间
    if (it != s.end () && (*it).l == pos) return it;//如果左端点就是它就不用拆,直接返回
    it--;//否则是上一个区间
    if ((*it).r < pos) return s.end ();//特判pos=n+1的情况
    int l = (*it).l, r = (*it).r, v = (*it).v;
    s.erase (it);
    s.insert (node{l, pos - 1, v});
    return s.insert (node{pos, r, v}).first;//小技巧,insert是有返回值的,是个pair,first就是迭代器
}

assign

找到要修改的区间,把这一段上的区间全都删掉并插入新的区间。

void assign(int l, int r, int x)
{
    snodeit itr = split (r + 1), itl = split (l);//先把两头的区间给拆了
    s.erase (itl, itr);//set 自带的函数,左闭右开
    s.insert (node{l, r, x});
}

注意这里,如果要保证正确性,一定要先拆右边再拆左边,否则拆右边时可能会把 itl 指向的区间删了,会出问题。

这就是珂朵莉树的基础操作,区间加之类的和 assign 同理,只不过改成遍历 \([itl,itr)\),每个区间加 \(x\)

注意这里的区间加,set 里的值是 \(const\) 的,不能直接修改,所以要像上面的结构体里那样,使用 mutable,这个可以使得 const 变量也可以修改。

例题

珂朵莉树板子(发源地)CF896C
CF1638E

posted @ 2026-01-02 10:39  wo2011  阅读(3)  评论(0)    收藏  举报
//雪花飘落效果