• 珂朵莉树


    前言:

    珂朵莉树($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$)

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

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

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

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

  • 相关阅读:
    web安全培训笔记
    《virtualbox完全学习手册》
    vim多标签,多窗口
    lnmp.org一键安装包
    git基本命令,Git的skil-map,git配置http/https/socks5代理,,,,,,,,,,,,,,,,,,,,,,
    大数据
    chinacloud大数据新闻
    CentOS6.5/7安装配置Samba
    java项目
    学习Java Web开发
  • 原文地址:https://www.cnblogs.com/Vscoder/p/10606870.html
Copyright © 2020-2023  润新知