Chtholly Tree (珂朵莉树) ODT

本文深入探讨了ODT(OldDriverTree)数据结构及其关键操作,包括区间加法、区间赋值、查找区间第K大值及区间值求和等功能。通过具体的C++实现代码,展示了如何高效地进行区间更新和查询,适用于处理大规模动态数据集。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ODT,OldDriverTreeChthollyTree O D T , O l d D r i v e r T r e e , 又 名 C h t h o l l y T r e e
− 关 键 操 作 推 平 一 段 区 间

1. CF 896C Willem, Chtholly and Seniorious

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int MOD7 = 1e9 + 7;
const int MOD9 = 1e9 + 9;
const int imax_n = 1e5 + 7;
//大数相加
LL add(LL a, LL b, LL mod)
{
    LL res = 0;
    LL ans = a;
    while (b)
    {
        if (b&1) res = (res + ans) % mod;
        ans = (ans + ans) % mod;
        b>>=1;
    }
    return res;
}
//快速幂
LL pow(LL a, LL b, LL mod)
{
    LL res = 1;
    LL ans = a % mod;
    while (b)
    {
        if (b&1) res = res * ans % mod;
        ans = ans * ans % mod;
        b>>=1;
    }
    return res;
}

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& o) const {
        return l < o.l;
    }
};

set<node> s;


// 拆分操作
set<node>::iterator split(int pos)
{
    auto it = s.lower_bound(node(pos));
    if (it != s.end() && it->l == pos) return it;
    --it;
    if (pos > it->r) return s.end();
    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;
    // pair<iterator, bool> insert(const value_type& val);
}


//区间加操作
void add(int l, int r, LL val=1)
{
    split(l);
    auto itr = split(r+1), itl = split(l);
    for (; itl != itr; ++itl) itl->v += val;
}

//区间赋值操作
void assign_val(int l, int r, LL val=0)
{
    split(l);
    auto itr = split(r+1), itl = split(l);
    s.erase(itl, itr);
    s.insert(node(l, r, val));
}


//求区间第K大值,reversed控制顺序与逆序
LL rank_(int l, int r, int k, bool reversed=0)
{
    vector<pair<LL, int>> vp;
    if (reversed) k = r - l + 2 - k;
    split(l);
    auto itr = split(r+1), itl = split(l);
    vp.clear();
    for (; itl != itr; ++itl)
        vp.push_back({itl->v, itl->r - itl->l + 1});
    sort(vp.begin(), vp.end());
    for (auto i: vp)
    {
        k -= i.second;
        if (k <= 0) return i.first;
    }
    return -1LL;
}


//区间值求和操作
LL sum(int l, int r, int ex, int mod)
{
    split(l);
    auto itr = split(r+1), itl = split(l);
    LL res = 0;
    for (; itl != itr; ++itl)
        res = (res + (LL)(itl->r - itl->l + 1) * pow(itl->v, LL(ex), LL(mod))) % mod;
    return res;
}

int n, m;
LL seed, vmax;

LL rnd()
{
    LL ret = seed;
    seed = (seed * 7 + 13) % MOD7;
    return ret;
}

LL a[imax_n];

int main()
{
    cin>>n>>m>>seed>>vmax;
    for (int i=1;i<=n;++i)
    {
            a[i] = (rnd() % vmax) + 1;
            s.insert(node(i,i,a[i]));
    }
    s.insert(node(n+1, n+1, 0));
    int lines = 0;
    for (int i =1; i <= m; ++i)
    {
        int op = int(rnd() % 4) + 1;
        int l = int(rnd() % n) + 1;
        int r = int(rnd() % n) + 1;
        if (l > r)
            swap(l,r);
        int x, y;
        // cout<<i<<"   op = "<<op<<"    ";
        if (op == 3)
            x = int(rnd() % (r-l+1)) + 1;
        else
            x = int(rnd() % vmax) +1;

        if (op == 4)
            y = int(rnd() % vmax) + 1;
        if (op == 1)
            add(l, r, LL(x));
        else if (op == 2)
            assign_val(l, r, LL(x));
        else if (op == 3)
        {
            // cout<<"lines"<<++lines<<endl;
            cout<<rank_(l, r, x)<<endl;
        }
        else
        {
            // cout<<"lines"<<++lines<<endl;
            cout<<sum(l, r, x, y)<<endl;
        }
    }
    return 0;
}
<think>嗯,用户让我介绍一下莉树及其发明者。首先,我需要回忆一下关于莉树的知识。莉树,英文名应该是Chtholly Tree,对吧?我记得它是一种数据结构,主要用于处理区间操作的题目,尤其是在线比赛中,比如编程竞赛中的一些问题。不过具体细节可能需要再仔细想想。 首先,关于发明者。莉树的名字听起来像是一个人的名字,但可能其实是来源于某个小说或动漫中的角色,因为有时候算法会被爱好者以角色命名。比如,我记得有个叫Chtholly的角色来自《末日时在做什么?有没有空?可以来拯救吗?》这部作品,可能因此这个数据结构被社区称为莉树。至于实际的发明者,可能并不是一个真实的人,而是由某个竞赛选手或社区成员提出并命名的。需要确认这一点是否正确,或者是否有相关的论文或原始出处。 接下来,关于莉树的结构和原理。它应该是一种基于平衡树或类似结构的数据结构,用于高效处理区间操作,比如区间赋值、区间查询等。不过更具体地说,莉树的核心思想是将区间分成多个块,每个块具有相同的值,这样在进行区间操作时,可以合并或分裂这些块,从而减少需要处理的数据量。例如,当进行区间赋值时,如果多个连续区间已经具有相同的值,可以合并成一个块,这样后续的查询或操作就可以更高效。 需要进一步解释它的实现方式。通常,莉树使用std::set或者类似的平衡树结构来维护这些区间块,每个节点代表一个区间[l, r),并且存储该区间的值。当进行操作时,会找到相关的区间块,进行分裂或合并,然后应用操作。例如,当有一个区间赋值操作时,可能需要将覆盖该区间的现有块拆分成几个部分,然后修改中间部分的块的值,并合并相邻的相同值的块,以保持结构的紧凑性。 然后,莉树的优缺点是什么?优点可能是在特定的场景下,比如当有大量的区间赋值操作,并且后续操作可以受益于这些合并后的块时,它的时间复杂度较低,接近O(log n)每次操作。但缺点可能是在最坏情况下,比如频繁的随机区间操作,导致块的数量增加,从而降低效率,甚至退化为O(n)的时间复杂度。因此,它通常适用于题目中存在较多区间赋值操作的情况,比如某些特定的编程竞赛题目。 关于应用场景,莉树常用于解决需要处理区间覆盖、区间统计等问题,例如区间颜色覆盖、区间和查询等,尤其是在这些操作具有特定的模式时。例如,在一个问题中,如果存在大量的区间赋值操作,并且后续的查询操作可以快速在块上完成,那么使用莉树会比较高效。 另外,需要举一个简单的例子来说明它的使用方式。比如,假设有一个数组初始时全部为0,然后多次进行操作:将区间[l, r]的值设置为v,或者查询区间[l, r]的和。使用莉树的话,每次赋值操作会将相关的区间合并成块,并记录每个块的值,而查询时只需要遍历覆盖查询区间的块,计算它们的贡献即可。这样,如果赋值操作导致很多连续的相同值块,查询就会变得更快。 最后,关于发明者的部分,可能需要再次确认。根据我之前的了解,莉树并没有一个正式的学术论文,而是起源于编程竞赛社区,由选手们在实际解题过程中提出并发展起来的。因此,它的名字来源于小说中的角色,而并非由某位特定的发明者正式提出。可能需要指出这一点,避免误导用户认为有明确的个人发明者。 总结一下,我需要将莉树的定义、结构、工作原理、优缺点、应用场景以及名字来源和背景整理成一个清晰的回答,用中文分步骤解释,确保符合用户的要求,同时保持LaTeX格式的正确使用。</think>### 莉树Chtholly Tree)介绍 #### 1. **基本概念** 莉树Chtholly Tree)是一种用于高效处理**区间操作**的数据结构,尤其适用于需要频繁区间赋值(如区间覆盖)的场景。其核心思想是**将区间划分为若干连续块**,每个块内的值相同,从而减少重复操作。 #### 2. **核心原理** - **区间分块**:用集合(如 `std::set`)维护区间块,每个块表示为三元组 $(l, r, v)$,表示区间 $[l, r)$ 的值为 $v$。 - **操作优化**: 当进行区间赋值时,将覆盖目标区间的块**拆分**,仅修改目标部分的块并**合并相邻相同值的块**,避免逐点操作。 #### 3. **实现步骤** 以区间赋值操作为例: 1. **拆分左边界**:找到包含左端点 $l$ 的块,将其拆分为 $[块起点, l)$ 和 $[l, 块终点)$。 2. **拆分右边界**:类似地拆分右端点 $r$。 3. **删除中间块**:移除完全落在 $[l, r)$ 内的块。 4. **插入新块**:添加新块 $[l, r)$ 并赋值为 $v$。 #### 4. **时间复杂度** - **平均情况**:$O(\log n)$ 每次操作(依赖块合并的效率)。 - **最坏情况**:$O(n)$(如频繁随机区间操作导致块无法合并)。 #### 5. **应用场景** - **区间覆盖**:如“将 $[l, r]$ 全部设为 $v$”。 - **区间统计**:如“求 $[l, r]$ 的和/最大值”。 - **典型题目**:CF896C(题目中设计了大量随机区间操作,适合莉树优化)。 #### 6. **优缺点** - **优点**: 代码简短(约50行),对特定问题(含大量区间覆盖)效率极高。 - **缺点**: 依赖数据随机性,若块无法合并,性能会显著下降。 #### 7. **发明背景** - **名称来源**: 名字源自轻小说《末日时在做什么?有没有空?可以来拯救吗?》中的角色**莉·诺塔·瑟尼欧里斯(Chtholly Nota Seniorious)**,由编程竞赛社区命名。 - **实际起源**: 未发表正式论文,最早出现在2017年Codeforces比赛[题解](https://codeforces.com/blog/entry/56135)中,用于解决CF896C。 #### 8. **代码片段示例** ```cpp struct Node { int l, r; mutable int v; // mutable允许在set中修改v Node(int l, int r, int v) : l(l), r(r), v(v) {} bool operator<(const Node& o) const { return l < o.l; } }; set<Node> odt; auto split(int pos) { auto it = odt.lower_bound(Node(pos, 0, 0)); if (it != odt.end() && it->l == pos) return it; --it; int l = it->l, r = it->r, v = it->v; odt.erase(it); odt.insert(Node(l, pos, v)); return odt.insert(Node(pos, r, v)).first; } void assign(int l, int r, int v) { auto end = split(r), begin = split(l); odt.erase(begin, end); odt.insert(Node(l, r, v)); } ``` #### 9. **总结** 莉树是竞赛编程中的“利器”,适用于特定区间操作场景,但其性能依赖数据特性。其命名体现了社区文化,是算法与二次元结合的典型例子。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值