珂朵莉树 | “朝闻道”知识分享大赛

重生带我走 2023-11-20 22:09:52

这是我参加朝闻道知识分享大赛的第十篇文章。

珂朵莉树(Chtholly Tree)起源于CF896C,那道题要求我们实现一种数据结构,可以较快地实现:

  • 区间加

  • 区间赋值

  • 求区间第k大值

  • 求区间n次方和

img

适用场景
大部分涉及到区间赋值操作的问题,都可以用珂朵莉树,对于一些可以暴力骗分的比赛(比如

img

篮球杯也是可以使用的!)

核心思想
把值相同的区间合并成一个结点保存在 set 里面。

下面给出模板题与模板代码(结尾处给出细节处理):
模板题:

C. Willem, Chtholly and Seniorious

Physical Education Lessons

应用:

E. Colorful Operations

封装珂朵莉树:

template<typename T>
struct ODT//珂朵莉树
{
    struct Node
    {
        int l,r;
        mutable T v;
        Node(int _l,int _r = -1,T _v = 0):l(_l),r(_r),v(_v){}
        bool operator<(const Node&o) const {return l < o.l;}
    };
    set<Node> S;
    void insert(int l,int r,T v){//往set中插入结点
        S.insert(Node(l,r,v));
    }
    auto split(int pos){//断开区间
        auto it = S.lower_bound(Node(pos));
        if(it != S.end() && it->l == pos)   return it;
        --it;
        int l = it->l,r = it->r;
        T v = it->v;
        S.erase(it);
        S.insert(Node(l,pos - 1,v));
        return S.insert(Node(pos,r,v)).first;
    }
    void add(int l,int r,T v){  //区间加
        for(auto end = split(r + 1),it = split(l);it != end;it++)
            it->v += v;
    }
    void assign(int l,int r,T v){   //区间赋值
        auto end = split(r + 1),begin = split(l);
        S.erase(begin,end);
        S.insert(Node(l,r,v));
    }
    T get_k(int l,int r,int k){ //区间第k大的数
        vector<pair<T,int>> v;
        for(auto end = split(r + 1),it = split(l);it != end;it++){
            v.push_back({it->v,it->r - it->l + 1});
        }
        sort(v.begin(),v.end());
        for(auto x : v){
            k -= x.second;
            if(k <= 0)  return x.first;
        }
        return -1;
    }
    T sum_pow(int l,int r,int k,int p){ //区间[l,r]的k次幂之和
        ll res = 0;
        for(auto end = split(r + 1),it = split(l);it != end;it++){
            res = (res + 1ll * (it->r - it->l + 1) * fpow(it->v,k,p) % p) % p;
        }
        return res;
    }
};

模板题代码:

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 1e5 + 10,mod = 1e9 + 7;
int n,m,seed,vmax;
int a[N];

ll fpow(ll a,ll b,ll p)
{
    a %= p;
    ll res = 1;
    while (b)
    {
        if(b & 1)   res = res * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return res;
}

template<typename T>
struct ODT//珂朵莉树
{
    struct Node
    {
        int l,r;
        mutable T v;
        Node(int _l,int _r = -1,T _v = 0):l(_l),r(_r),v(_v){}
        bool operator<(const Node&o) const {return l < o.l;}
    };
    set<Node> S;
    void insert(int l,int r,T v){//往set中插入结点
        S.insert(Node(l,r,v));
    }
    auto split(int pos){//断开区间
        auto it = S.lower_bound(Node(pos));
        if(it != S.end() && it->l == pos)   return it;
        --it;
        int l = it->l,r = it->r;
        ll v = it->v;
        S.erase(it);
        S.insert(Node(l,pos - 1,v));
        return S.insert(Node(pos,r,v)).first;
    }
    void add(int l,int r,T v){  //区间加
        for(auto end = split(r + 1),it = split(l);it != end;it++)
            it->v += v;
    }
    void assign(int l,int r,T v){   //区间赋值
        auto end = split(r + 1),begin = split(l);
        S.erase(begin,end);
        S.insert(Node(l,r,v));
    }
    T get_k(int l,int r,int k){ //区间第k大的数
        vector<pair<T,int>> v;
        for(auto end = split(r + 1),it = split(l);it != end;it++){
            v.push_back({it->v,it->r - it->l + 1});
        }
        sort(v.begin(),v.end());
        for(auto x : v){
            k -= x.second;
            if(k <= 0)  return x.first;
        }
        return -1;
    }
    T sum_pow(int l,int r,int k,int p){ //区间[l,r]的k次幂之和
        ll res = 0;
        for(auto end = split(r + 1),it = split(l);it != end;it++){
            res = (res + 1ll * (it->r - it->l + 1) * fpow(it->v,k,p) % p) % p;
        }
        return res;
    }
};

int rnd()
{
    int ret = seed;
    seed = (seed * 7ll + 13) % mod;
    return ret;
}

int main()
{
    cin.tie(nullptr)->sync_with_stdio(false);

    cin >> n >> m >> seed >> vmax;
    ODT<ll> odt;
    for(int i = 1;i <= n;i++)   
    {
        a[i] = (rnd() % vmax) + 1;
        odt.insert(i,i,a[i]);
    }

    int op,l,r,x,y;
    while (m--)
    {
        op = (rnd() % 4) + 1;
        l = (rnd() % n) + 1;
        r = (rnd() % n) + 1;
        
        if(l > r)   swap(l,r);
        if(op == 3)
        {
            x = (rnd() % (r - l + 1)) + 1;
            cout << odt.get_k(l,r,x) << "\n";
        }
        else
        {
            x = (rnd() % vmax) + 1;
            if(op == 1) odt.add(l,r,x);
            else if(op == 2)    odt.assign(l,r,x);
            else
            {
                y = (rnd() % vmax) + 1;
                cout << odt.sum_pow(l,r,x,y) << "\n";
            }
        }
    }
    
    return 0;
}

注意: 往集合里插入结构体的时候,如果有缺省的构造函数,即如下:

Node(int _l,int _r = -1,ll _v = 0):l(_l),r(_r),v(_v){}

并且使用如下方式插入:

S.insert({i,i,a[i]});

则会出现问题!即它只接收我第一个参数,后面的全给忽视了!!!

img

因此一定要注意插入的时候用带上结构体。当然如果不定义构造函数,就可以直接用大括号插入。

...全文
116 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

1,034

社区成员

发帖
与我相关
我的任务
社区描述
中南民族大学CSDN高校俱乐部聚焦校内IT技术爱好者,通过构建系统化的内容和运营体系,旨在将中南民族大学CSDN社区变成校内最大的技术交流沟通平台。
经验分享 高校 湖北省·武汉市
社区管理员
  • c_university_1575
  • WhiteGlint666
  • wzh_scuec
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

欢迎各位加入中南民族大学&&CSDN高校俱乐部社区(官方QQ群:908527260),成为CSDN高校俱乐部的成员具体步骤(必填),填写如下表单,表单链接如下:
人才储备数据库及线上礼品发放表单邀请人吴钟昊:https://ddz.red/CSDN
CSDN高校俱乐部是给大家提供技术分享交流的平台,会不定期的给大家分享CSDN方面的相关比赛以及活动或实习报名链接,希望大家一起努力加油!共同建设中南民族大学良好的技术知识分享社区。

注意:

1.社区成员不得在社区发布违反社会主义核心价值观的言论。

2.社区成员不得在社区内谈及政治敏感话题。

3.该社区为知识分享的平台,可以相互探讨、交流学习经验,尽量不在社区谈论其他无关话题。

试试用AI创作助手写篇文章吧