珂朵莉树

前言:

珂朵莉树($ODT$),与其说它是数据结构,不如说它是暴力

它可以代替线段树实现某些区间操作

复杂度嘛 $O($不对$)$ 吧

正文:

SET

要学珂朵莉树,首先要会使用 $STL$ 的 $set$

$set$ 是一个集合,它会将其中的元素自动排序与去重

$ps:$ 如果需要可重集,请使用 $multiset$

比较常用的函数有

$s.insert()$ 在集合中插入此元素

$s.erase()$ 删除集合中的元素

$s.size()$ 返回集合中的元素个数

$s.empty()$ 如果集合为空返回 1,否则返回 0

$s.clear()$ 清除所有元素

$s.begin()$ 返回指向第一个元素的迭代器

$s.end()$ 返回指向最后一个元素的迭代器

$s.find()$ 在集合中查找此元素,如找到则返回该元素的迭代器,否则返回 $s.end()$

$s.upper\_bound()$ 返回集合中二分查找到的第一个大于此元素的迭代器

$s.lower\_bound()$ 返回集合中二分查找到的第一个大于等于此元素的迭代器

想看更详细的 $set$ 介绍,请点击这里

接下来可以搞珂朵莉珂朵莉树了

节点信息

首先我们来看 $set$ 中的每个节点所维护的信息

struct node
{
    int l,r;
    mutable ll v;
    node(int l,int r=-1,ll v=0):l(l),r(r),v(v){}
    bool operator < (const node &a) const
    {
        return l<a.l;
    }
};

每一个 $node$ 维护了 3 个信息 $l,r,v$

它的含义是 $l$ 到 $r$ 区间的权值都为 $v$

$mutable$ 声明之后,我们将可以修改这个权值 $v$,否则会 $CE$

然后我们重载一下,按 $l$ 从小到大排序,并扔到 $set$ 里

set<node>s;
#define IT set<node>::iterator

核心操作

接下来是珂朵莉树的核心操作

IT split(int pos)
{
    IT it=s.lower_bound(node(pos));//找到首个l不小于pos的节点
    if(it!=s.end()&&it->l==pos) return it;//如果pos是某个节点的左端点,直接返回该节点
    it--;//否则pos一定在前一个区间中
    int l=it->l,r=it->r;//[l,r]就是要被分割的区间
    ll v=it->v;//取出这个节点的值
    s.erase(it);//删除原节点
    s.insert(node(l,pos-1,v));//插入前半段
    return s.insert(node(pos,r,v)).first;//插入后半段,返回后半段的迭代器
}

这个操作的作用是将原来含有 $pos$ 位置的节点切为两段区间 $[l,pos-1]$ 和 $[pos,r]$

关于最后的返回值,$SuperJvRuo$ 说是利用了 pair<iterator,bool> insert (const value_type& val) 的返回值

我不知道,我一定不知道

void assign_num(int l,int r,ll num)
{
    IT itr=split(r+1),itl=split(l);
    s.erase(itl,itr);
    s.insert(node(l,r,num));
}

这个操作可以推平一段区间

先把 $l$ 和 $r+1$ 进行 $split$

注意这里要先切 $r+1$ 后切 $l$

然后我们用 $s.erase()$ 删除 $[l,r+1)$ 之间的节点

再插入节点 $node(l,r,num)$

这样就可以将 $[l,r]$ 区间的权值全部修改为 $num$

此操作可以保证珂朵莉树的复杂度

当然是在数据随机的情况下

其他操作

区间加

void add(int l,int r,ll num)
{
    IT itr=split(r+1),itl=split(l);
    for(;itl!=itr;itl++)
        itl->v+=num;
}

区间求和

ll get_sum(int l,int r)
{
    ll ans=0;
    IT itr=split(r+1),itl=split(l);
    for(;itl!=itr;itl++)
        ans+=itl->v*1LL*(itl->r-itl->l+1);
    return ans;
}

区间第 $k$ 大

ll get_kth(int l,int r,int k)
{
    vector< pair<ll,int> >vec;
    IT itr=split(r+1),itl=split(l);
    for(;itl!=itr;itl++)
        vec.push_back(pair<ll,int>(itl->v,itl->r-itl->l+1));
    std::sort(vec.begin(),vec.end());
    for(vector< pair<ll,int> >::iterator it=vec.begin();it!=vec.end();it++)
    {
        k-=it->second;
        if(k<=0) return it->first;
    }
}

这些都是一些极其暴力的操作

不过是真的好写好理解啊~~~

后序:

关于它的名字($Old\ Driver\ Tree$)

什么老司机树,旧驱动树啊

我并不知道这些都是怎么来的

还有考试的时候好像一般都不敢写珂朵莉树

虽然它好写,但它可以用来骗分啊!!!

posted @ 2019-03-27 13:22  Polaris5452830  阅读(261)  评论(3)    收藏  举报