欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

神奇的暴力数据结构——ODT

程序员文章站 2022-03-03 19:48:49
...

前言

\(ODT\),即珂朵莉树,又称老司机树\(Old\ Driver\ Tree\))。

它是一个十分暴力的数据结构,可以用于各种乱搞,也非常的实用。

当然,这全要基于一个基本条件:数据随机

主要思想

\(ODT\)的主要思想就是把一个元素完全相同的区间合并成一个节点,然后用\(set\)维护(我也不知道为什么称其为“树”)。

而在数据随机的情况下,节点的期望个数是很少的,因此复杂度也就比较低。

核心操作: \(Split\)操作

\(ODT\)的核心操作就是\(Split\)操作。

\(Split(x)\)的作用是分裂出一个以\(x\)为左端点的区间

对于这个操作,我们先用\(set\)\(lower\_bound\)函数,找到左端点不小于\(x\)的第一个区间。

如果此时找到的区间左端点已经为\(x\)了,则直接返回这个区间。

否则,我们就将迭代器移动到上一个位置,而这个区间才是我们要分裂的。

设这个区间为\([l,r]\)

则我们应将它分裂成\([l,x-1]\)\([x,r]\)两部分。

所以我们先将\(l,r\)用变量存储下来,然后在\(set\)中清除原来的区间,并将新的区间插入\(set\)

然后\([x,r]\)这个区间就是我们所要找的,将其返回即可。

这个操作代码如下:

I IT Sp(CI x)//分裂出一个以x为左端点的区间
{
    IT t;if((t=S.lower_bound(Il(x)))!=S.end()&&!(t->l^x)) return t;//如果此时找到的区间左端点已经为x了,则直接返回这个区间
    RI l=(--t)->l,r=t->r,v=t->v;S.erase(t),S.insert(Il(l,x-1,v));//将迭代器移动到上一个位置,先将l,r用变量存储下来,然后在set中清除原来的区间,并将新的区间插入set
    return S.insert(Il(x,r,v)).first;//区间[x,r]就是我们所要找的,将其返回即可
}

推平操作:\(Assign\)操作

显然,光有\(Split\)操作显然会\(T\)飞。

所以我们就需要一个推平操作,把某段区间合并成一个节点。

则我们把这个区间的左端点,以及右端点的下一个位置提出,然后删除它们之间的所有节点(包括左端点但不包括右端点的下一个位置),再把新的节点加入即可。

这个操作代码如下:

I void Assign(CI x,CI y,CI v)//推平操作
{
    IT tr=Sp(y+1),tl=Sp(x);S.erase(tl,tr),S.insert(Il(x,y,v));//把这个区间左端点及其之后、右端点下个位置之前的所有节点删除,然后插入新的节点
}

其余操作

其余操作就没什么好说的了,直接暴力扫一遍即可,真是不能再暴力了。

例题

下面给出一道例题:【BZOJ1858】[SCOI2010] 序列操作(ODT裸题)(正解是线段树)。

转载于:https://www.cnblogs.com/chenxiaoran666/p/ODT.html