<更新提示>

<第一次更新>


<正文>

珂朵莉树

珂朵莉树其实不是树,只是一个借助平衡树实现的数据结构,主要是对于有区间赋值的数据结构题,可以用很暴力的代码很高效地完成任务,当然这是建立在数据随机的基础上的。

即使数据不是随机的,写一个珂朵莉树用来当做暴力也是很划算的。

珂朵莉树可以使用std::map来实现,并且代码量极小,不知道为什么大家都要写set

维护方式

那么问题来了,如何使用\(map\)来实现珂朵莉树呢?众所周知,用\(set\)的实现方式都是维护一个连续的相同区间,\(map\)当然也是同理,我们仍然用\(map\)的第一维当数组下标,把相同一段元素的值存在段下标的最后一个位置,就比如这样:

我们只在红色数字的下标位置申请\(map\)空间,存储数值。然后我们就可以非常方便的操作\(map\)了,看具体操作吧。

整体的定义和申明如下:

#define it map<int,int>::iterator
struct ChthollyTree
{
map <int,int> s;
inline int find(int p) { }
inline void split(int p) { }
inline pair<it,it> range(int l,int r) { }
};

find

我们需要实现珂朵莉树的\(find\)操作,就是在\(map\)中定位原数列一个位置的值。只需要使用\(map\)的\(lower\_bound\)操作找到段尾的位置即可,非常简单,没什么好说的。

inline int find(int p) { return s.lower_bound(p) -> second; }

spilt

珂朵莉树的\(split\)操作就是分裂一个区间,这方便我们维护原序列的区间操作。实现也非常简单,只需要直接调用\(find\)函数,再根据\(map\)维护段的定义,在最后一个位置申请空间,赋上值即可。

inline void split(int p)
{
int pos = find(p);
s[p] = pos;
}

值得注意的是,使用一个变量\(pos\)来记录\(find\)函数的结果是必须的,如果直接写\(s[p]=find(p)\)就会产生奇奇怪怪的错误

为什么?首先,当我们写\(s[p]=find(p)\)时,\(find(p)\)函数还没有被调用,但是由于\(s[p]\),\(map\)当中已经申请了一个二元组的位置\((p,0)\),这样再调用\(find(p)\),\(lower\_bound\)函数找到的值就不正确了。

难道等赋值号的结合律不是从右向左吗?原本是的,但是实测确实会有这样的问题,保险起见,我们使用\(STL\)容器的时候尽量不在一句话中同时调用和查找。

range

珂朵莉树的\(range\)操作就是提取出一整个完整的区间,并返回\(map\)的首尾迭代器,这样就可以执行具体的区间操作了。实现方法就是调用两次\(split\)函数。

inline pair<it,it> range(int l,int r)
{
split(l-1) , split(r);
return make_pair( s.find(l-1) , s.find(r) );
}

然后呢? 没有然后了,我们已经实现珂朵莉树的全部功能了。

完整代码如下:

struct ChthollyTree
{
map <int,int> s;
inline int find(int p) { return s.lower_bound(p) -> second; }
inline void split(int p)
{
int pos = find(p);
s[p] = pos;
}
inline pair<it,it> range(int l,int r)
{
split(l-1) , split(r);
return make_pair( s.find(l-1) , s.find(r) );
}
};
ChthollyTree T;

那么如何实现具体的区间操作呢?别急,我们还需要看两种珂朵莉树的遍历方式。

完整遍历:区间修改

我们可以利用\(range\)函数提取区间,实现对一个区间在\(map\)中的完整遍历,从而实现区间修改操作:

map <int,int> :: iterator st,ed;
pair<it,it> border = T.range(l,r);
st = border.first , ed = border.second;
while ( st != ed ) ed->second += x , ed--;

如上就是一个区间加法的示例,我们先提取区间,然后用迭代器遍历\(map\),再修改元素即可。

一般遍历:获取信息

当我们需要计算某一个区间的信息值时,不一定要提取区间,可以使用更好的方式遍历区间,直接计算即可:

ll sum = 0; int next;
for (int i=l;i<=r;i=next+1)
{
it now = T.s.lower_bound(i);
next = min( r , now->first );
ll powsum = mul( next - i + 1 , quickpow( now->second , x , y ) , y );
Add( sum , powsum , y );
}
printf("%lld\n",sum);

如上就是一个查询区间\(x\)次方和模\(y\)的示例,我们可以不断查询下一个段边界的迭代器,然后直接计算。

然后一个完整的珂朵莉树就得到实现了,剩下的操作也都可以通过上述的两种遍历实现,只要暴力就可以了。

Willem, Chtholly and Seniorious

Description

请你写一种奇怪的数据结构,支持:

  • 1 l r x :将[l,r]区间所有数加上x
  • 2 l r x :将[l,r]区间所有数改成x
  • 3 l r x :输出将[l,r]区间从小到大排序后的第x个数是的多少(即区间第x小,数字大小相同算多次,保证 1≤ x ≤ r-l+1)
  • 4 l r x y :输出[l,r]区间每个数字的x次方的和模y的值

Input Format

这道题目的输入格式比较特殊,需要选手通过seed自己生成输入数据。 输入一行四个整数n,m,seed,vmax(1≤ n,m≤10^5 ,0≤seed≤10^9+7 ,1≤vmax≤10^9 ) 其中n表示数列长度,m表示操作次数,后面两个用于生成输入数据。 数据生成的伪代码如下

def rnd():

    ret = seed
seed = (seed * 7 + 13) mod 1000000007
return ret for i = 1 to n: a[i] = (rnd() mod vmax) + 1 for i = 1 to m: op = (rnd() mod 4) + 1
l = (rnd() mod n) + 1
r = (rnd() mod n) + 1 if (l > r):
swap(l, r) if (op == 3):
x = (rnd() mod (r - l + 1)) + 1
else:
x = (rnd() mod vmax) + 1 if (op == 4):
y = (rnd() mod vmax) + 1

Output Format

对于每个操作3和4,输出一行仅一个数。

Sample Input

10 10 9 9

Sample Output

1 1 3 3

解析

就是一道模板题嘛,数据都是自己随机生成的。区间第\(x\)小的数字可以把数字段全部都取出来,然后排一下序二分查找。

\(Code:\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define it map<int,ll>::iterator
const int N = 1e5+20 , Mod = 1e9+7;
struct ChthollyTree
{
map <int,ll> s;
inline ll find(int p) { return s.lower_bound(p) -> second; }
inline void split(int p)
{
ll pos = find(p);
s[p] = pos;
}
inline pair<it,it> range(int l,int r)
{
split(l-1) , split(r);
return make_pair( s.find(l-1) , s.find(r) );
}
};
ChthollyTree T;
struct sec
{
ll num; int cnt;
sec (ll _num = 0,int _cnt = 0) { num = _num , cnt = _cnt; }
friend bool operator < (sec p1,sec p2) { return p1.num < p2.num; }
};
int n,m,seed,vmax,a[N],sum[N],len;
sec p[N];
inline int random(void)
{
int res = seed;
seed = ( seed * 7LL + 13LL ) % Mod;
return res;
}
inline ll add(ll a,ll b,ll mod) { return a + b >= mod ? a + b - mod : a + b; }
inline ll mul(ll a,ll b,ll mod) { return a * b % mod; }
inline void Add(ll &a,ll b,ll mod) { a = add( a , b , mod ); }
inline void Mul(ll &a,ll b,ll mod) { a = mul( a , b , mod ); }
inline ll quickpow(ll a,ll b,ll mod)
{
ll res = 1; a %= mod;
for ( ; b ; Mul(a,a,mod) , b>>=1 )
if ( 1 & b ) Mul(res,a,mod);
return res;
}
inline void input(void)
{
scanf("%d%d%d%d",&n,&m,&seed,&vmax);
for (int i=1;i<=n;i++)
{
a[i] = random() % vmax + 1;
T.s[i] = a[i];
}
T.s[0] = T.s[n+1] = 0;
}
inline void solve(void)
{
int op,l,r,x,y; int cnt=0;
for (int i=1;i<=m;i++)
{
op = random() % 4 + 1;
l = random() % n + 1 , r = random() % n + 1;
if ( l > r ) swap( l , r );
if ( op == 3 ) x = random() % (r-l+1) + 1;
else x = random() % vmax + 1;
if ( op == 4 ) y = random() % vmax + 1;
map <int,ll> :: iterator st,ed;
if ( op == 1 )
{
pair<it,it> border = T.range(l,r);
st = border.first , ed = border.second;
while ( st != ed ) ed->second += x , ed--;
}
if ( op == 2 )
{
pair<it,it> border = T.range(l,r);
st = border.first , ed = border.second;
while ( st != ed ) T.s.erase( ed-- );
T.s[r] = x;
}
if ( op == 3 )
{
int next; len = 0;
for (int i=l;i<=r;i=next+1)
{
it now = T.s.lower_bound(i);
next = min( r , now->first );
p[++len] = sec( now->second , next-i+1 );
}
sort( p+1 , p+len+1 );
for (int i=1;i<=len;i++) sum[i] = sum[i-1] + p[i].cnt;
int l = 1 , r = len;
while ( l + 1 < r )
{
int mid = l + r >> 1;
if ( x <= sum[mid] ) r = mid;
else l = mid;
}
if ( x <= sum[r] && x > sum[l] ) printf("%lld\n",p[r].num);
else printf("%lld\n",p[l].num);
}
if ( op == 4 )
{
ll sum = 0; int next;
for (int i=l;i<=r;i=next+1)
{
it now = T.s.lower_bound(i);
next = min( r , now->first );
ll powsum = mul( next - i + 1 , quickpow( now->second , x , y ) , y );
Add( sum , powsum , y );
}
printf("%lld\n",sum);
}
}
}
int main(void)
{
input();
solve();
return 0;
}

<后记>

『珂朵莉树 Old Driver Tree』的更多相关文章

  1. 珂朵莉树(Chtholly Tree)学习笔记

    珂朵莉树(Chtholly Tree)学习笔记 珂朵莉树原理 其原理在于运用一颗树(set,treap,splay......)其中要求所有元素有序,并且支持基本的操作(删除,添加,查找......) ...

  2. [转]我的数据结构不可能这么可爱!——珂朵莉树(ODT)详解

    参考资料: Chtholly Tree (珂朵莉树) (应某毒瘤要求,删除链接,需要者自行去Bilibili搜索) 毒瘤数据结构之珂朵莉树 在全是珂学家的珂谷,你却不知道珂朵莉树?来跟诗乃一起学习珂朵 ...

  3. 珂朵莉树(ODT)笔记

    珂朵莉树,又叫老司机树($Old\, Driver \, Tree$) 是一种暴力出奇迹,就怕数据不随机的数据结构. 适用 需要用线段树维护一些区间修改的信息…… 像是区间赋值(主要),区间加…… 原 ...

  4. 「学习笔记」珂朵莉树 ODT

    珂朵莉树,也叫ODT(Old Driver Tree 老司机树) 从前有一天,珂朵莉出现了... 然后有一天,珂朵莉树出现了... 看看图片的地址 Codeforces可还行) 没错,珂朵莉树来自Co ...

  5. 题解 P3372 【【模板】线段树 1】(珂朵莉树解法)

    这道题可以用珂朵莉树做,但是由于数据比较不随机,而我也没有手写一颗平衡树,所以就被卡掉了,只拿了70分. 珂朵莉树是一种基于平衡树的(伪)高效数据结构. 它的核心操作是推平一段区间. 简而言之,就是把 ...

  6. 洛谷AT2342 Train Service Planning(思维,动态规划,珂朵莉树)

    洛谷题目传送门 神仙思维题还是要写点东西才好. 建立数学模型 这种很抽象的东西没有式子描述一下显然是下不了手的. 因为任何位置都以\(k\)为周期,所以我们只用关心一个周期,也就是以下数都在膜\(k\ ...

  7. 洛谷P4344 [SHOI2015]脑洞治疗仪(珂朵莉树)

    传送门 看到区间推倒……推平就想到珂朵莉树 挖脑洞直接assign,填坑先数一遍再assign再暴力填,数数的话暴力数 //minamoto #include<iostream> #inc ...

  8. 洛谷P2787 语文1(chin1)- 理理思维(珂朵莉树)

    传送门 一看到区间推倒……推平操作就想到珂朵莉树 区间推平直接assign,查询暴力,排序的话开一个桶统计,然后一个字母一个字母加就好了 开桶统计的时候忘了保存原来的左指针然后挂了233 //mina ...

  9. 洛谷P2082 区间覆盖(加强版)(珂朵莉树)

    传送门 虽然是黄题而且还是一波离散就能解决的东西 然而珂朵莉树还是很好用 相当于一开始区间全为0,然后每一次区间赋值,问最后总权值 珂朵莉树搞一搞就好了 //minamoto #include< ...

随机推荐

  1. 零零总总遇到过的CSS 样式

    1:添加弹出框阴影 2:禁止文本域缩放 3:直接使用CSS 完成文本内容大小写(针对英文) 4: 文本框中的占位符 5:让table每列一样高 6:不使用js 让内容换行 word-break 7:曾 ...

  2. python基础(34):线程(二)

    1. python线程 1.1 全局解释器锁GIL Python代码的执行由Python虚拟机(也叫解释器主循环)来控制.Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行.虽然 Py ...

  3. 五个常用的CSS简写

    1,margin/padding. (演示仅为margin,padding同理,需注意的是padding没有auto) 2.background. background: [background-co ...

  4. Java性能之synchronized锁的优化

    synchronized / Lock 1.JDK 1.5之前,Java通过synchronized关键字来实现锁功能 synchronized是JVM实现的内置锁,锁的获取和释放都是由JVM隐式实现 ...

  5. 远程连接docker

    vim /usr/lib/systemd/system/docker.service ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix: ...

  6. HTTP中的2XX状态码

    HTTP状态码分类 1XX --信息,服务器收到请求,需要请求者继续执行操作 2XX--成功,操作被成功接收并处理 3XX--重定向,需要进一步的操作以完成请求 4XX--客户端错误,请求包含语法错误 ...

  7. iOS UIPopoverView的使用

    UIViewController *contentViewController = [[UIViewController alloc] init]; contentViewController.vie ...

  8. python中Socket的使用

    说明 前一段时间学习python网络编程,完成简单的通过python实现网络通信的功能.现在,将python中Socket 通信的基本实现过程做一个记录备份. Socket通信 python 中的so ...

  9. Android Monkey使用

    Monkey 是什么? Android SDK自带的压力测试工具,也是一个命令行工具.它向系统发送伪随机的用户事件流(如按键输入,触摸屏输入,手势输入等),实现对正在开发的应用程序进行压力测试. (1 ...

  10. 024.微服务架构之服务注册与发现(kubernetes / SpringCloud)

    微服务 微服务是一种架构模式,一种分布式的架构风格. 顾名思义,micro service,将一个庞大的单体应用拆分成若干个“微小”的服务,服务间通过进程通讯完成原本在单体应用中的调用. 其中必要的六 ...