淺談珂朵莉樹
阿新 • • 發佈:2020-07-16
## 什麼是珂朵莉樹
珂朵莉樹,又稱$Old Driver Tree(ODT)$(老司機樹)。
是一種基於$set$的~~暴力~~資料結構。
因此,再學習珂朵莉樹之前,要掌握一些$set$和迭代器的知識
## 珂朵莉樹的適用範圍
線段樹能幹的它都能幹(~~只要你不怕T~~)
使一整段區間內的東西變得一樣,**資料隨機**
比如下面這一道題
## 起源題:CF896C
### 題目描述
![](https://cdn.luogu.com.cn/upload/image_hosting/1pqjy2jn.png)
### 分析
如果只有前$3$個操作,那麼別的資料結構似乎還可以使用
但是第$4$個操作是把區間中的每一個數拿出來進行運算
這樣的操作其它資料結構很難勝任
這時我們就要用到珂朵莉樹
### 定義
珂朵莉樹是把連續的一段值相同的區間當作一個節點對待
因此節點定義如下
``` cpp
struct asd{
ll l,r;
//節點的左右端點
mutable ll val;
//節點的權值,如果不加mutable則在初始化後無法進行修改
bool operator < (const asd& A)const{
return l::iterator
```
首先,我們要巨集定義$set$的迭代器
~~如果你不怕麻煩每次手打也可以~~
然後,我們再通過$set$建立一棵樹
``` cpp
set s;
```
(set是C++自帶的平衡樹,這就是珂朵莉樹是一棵樹的原因)
最後是程式碼
``` cpp
sit Split(ll wz){
//返回值型別為迭代器
sit it=s.lower_bound(asd(wz));
//查詢第一個左端點編號大於等於wz的節點
if(it!=s.end() && it->l==wz) return it;
//如果該節點的左端點是我們要分裂的節點,直接返回
it--;
//否則分裂上一個
ll l=it->l,r=it->r,val=it->val;
s.erase(it);
//將該節點拆分為兩個
s.insert(asd(l,wz-1,val));
return s.insert(asd(wz,r,val)).first;
//返回分裂位置的迭代器
}
```
### 複雜度的保證Assign
將一個區間推平,賦成相同的值
``` cpp
void Assign(ll l,ll r,ll val){
sit it2=Split(r+1),it1=Split(l);
s.erase(it1,it2);
//刪除區間[l,r+1)中所有的節點
s.insert(asd(l,r,val));
//插入新節點
}
```
### 其它操作
~~一個比一個暴力~~
#### 區間加
把$[l,r]$中的節點取出,分別加上就好了
這裡有一個細節必須注意,必須先宣告$it2$再宣告$it1$
否則根據$split$中的$erase$操作,迭代器$it1$可能會失效。
(因為$it1$所屬的節點可能被刪除了)
``` cpp
void ad(ll l,ll r,ll val){
sit it2=Split(r+1),it1=Split(l);
for(sit it=it1;it!=it2;++it){
it-> val+=val;
}
}
```
#### 區間第k小
把$[l,r]$中的節點取出,$sort$一下就行了
``` cpp
ll kth(ll l,ll r,ll k){
sit it2=Split(r+1),it1=Split(l);
vector >a;
a.clear();
for(sit it=it1;it!=it2;it++){
a.push_back(make_pair(it->val,it->r-it->l+1));
}
sort(a.begin(),a.end());
for(ll i=0;i>=1;
}
return ans%mod;
}
ll cx(ll l,ll r,ll x,ll y){
sit it2=Split(r+1),it1=Split(l);
ll ans=0;
for(sit it=it1;it!=it2;it++){
ans=(ans+(it->r-it->l+1)*ksm(it->val,x,y)%y)%y;
}
return ans;
}
```
### 完整程式碼
``` cpp
#include
using namespace std;
typedef long long ll;
#define sit set::iterator
const ll maxn=1e6+5;
struct asd{
ll l,r;
mutable ll val;
bool operator < (const asd& A)const{
return l s;
sit Split(ll wz){
sit it=s.lower_bound(asd(wz));
if(it!=s.end() && it->l==wz) return it;
it--;
ll l=it->l,r=it->r,val=it->val;
s.erase(it);
s.insert(asd(l,wz-1,val));
return s.insert(asd(wz,r,val)).first;
}
void Assign(ll l,ll r,ll val){
sit it2=Split(r+1),it1=Split(l);
s.erase(it1,it2);
s.insert(asd(l,r,val));
}
void ad(ll l,ll r,ll val){
sit it2=Split(r+1),it1=Split(l);
for(sit it=it1;it!=it2;++it){
it->val+=val;
}
}
ll kth(ll l,ll r,ll k){
sit it2=Split(r+1),it1=Split(l);
vector >a;
a.clear();
for(sit it=it1;it!=it2;it++){
a.push_back(make_pair(it->val,it->r-it->l+1));
}
sort(a.begin(),a.end());
for(ll i=0;i>=1;
}
return ans%mod;
}
ll cx(ll l,ll r,ll x,ll y){
sit it2=Split(r+1),it1=Split(l);
ll ans=0;
for(sit it=it1;it!=it2;it++){
ans=(ans+(it->r-it->l+1)*ksm(it->val,x,y)%y)%y;
}
return ans;
}
ll n,m,mmax,seed;
ll rnd(){
ll ret=seed;
seed=(seed*7+13)%1000000007;
return ret;
}
int main(){
scanf("%lld%lld%lld%lld",&n,&m,&seed,&mmax);
for(ll i=1;i<=n;i++){
ll aa=rnd()%mmax+1;
s.insert(asd(i,i,aa));
}
s.insert(asd(n+1,n+1,0));
for(ll i=1;i<=m;i++){
ll l,r,x,y;
ll 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;
else x=rnd()%mmax+1;
if(op==4) y=rnd()%mmax+1;
if(op==1) ad(l,r,x);
else if(op==2) Assign(l,r,x);
else if(op==3) printf("%lld\n",kth(l,r,x));
else printf("%lld\n",cx(l,r,x,y));
}
return 0;
}
```
### 複雜度證明
[傳送門](https://zhuanlan.zhihu.com/p/102786071)
## 其它題目
CF343D Water Tree(珂朵莉樹上樹)
CF915E Physical Education Lessons(正解為動態開點線段樹)
P4979 礦洞:坍塌
P1558 色板遊戲
P3740 貼海報
P5350 序列
P1204 擠牛奶Milking Cows