淺談珂朵莉樹


什么是珂朵莉樹

珂朵莉樹,又稱\(Old Driver Tree(ODT)\)(老司機樹)。

是一種基於\(set\)暴力數據結構。

因此,再學習珂朵莉樹之前,要掌握一些\(set\)和迭代器的知識

珂朵莉樹的適用范圍

線段樹能干的它都能干(只要你不怕T

使一整段區間內的東西變得一樣,數據隨機

比如下面這一道題

起源題:CF896C

題目描述

分析

如果只有前\(3\)個操作,那么別的數據結構似乎還可以使用

但是第\(4\)個操作是把區間中的每一個數拿出來進行運算

這樣的操作其它數據結構很難勝任

這時我們就要用到珂朵莉樹

定義

珂朵莉樹是把連續的一段值相同的區間當作一個節點對待

因此節點定義如下

struct asd{
    ll l,r;
    //節點的左右端點
    mutable ll val;
    //節點的權值,如果不加mutable則在初始化后無法進行修改
    bool operator < (const asd& A)const{
        return l<A.l;
    }
    //按照左端點從小到大的順序排序
    asd(ll aa,ll bb,ll cc){
        l=aa,r=bb,val=cc;
    }
    asd(ll aa){
        l=aa;
    }
};

核心操作 Split

這個操作的主要作用是將一個區間拆分開

比如我們要查詢區間\([3,7]\),但是\(3\)\(7\)並不是一個節點,因此我們要把它們從原有的節點中拆分出來

#define sit set<asd>::iterator

首先,我們要宏定義\(set\)的迭代器

如果你不怕麻煩每次手打也可以

然后,我們再通過\(set\)建立一棵樹

set<asd> s;

(set是C++自帶的平衡樹,這就是珂朵莉樹是一棵樹的原因)

最后是代碼

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

將一個區間推平,賦成相同的值

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\)所屬的節點可能被刪除了)

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\)一下就行了

ll kth(ll l,ll r,ll k){
    sit it2=Split(r+1),it1=Split(l);
    vector<pair<ll,ll> >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<a.size();i++){
        k-=a[i].second;
        if(k<=0) return a[i].first;
    }
}

區間冪次和

暴力維護+快速冪

ll ksm(ll ds,ll zs,ll mod){
    ll now=ds%mod,ans=1;
    while(zs){
        if(zs&1) ans=ans*now%mod;
        now=now*now%mod;
        zs>>=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;
}

完整代碼

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define sit set<asd>::iterator
const ll maxn=1e6+5;
struct asd{
    ll l,r;
    mutable ll val;
    bool operator < (const asd& A)const{
        return l<A.l;
    }
    asd(ll aa,ll bb,ll cc){
        l=aa,r=bb,val=cc;
    }
    asd(ll aa){
        l=aa;
    }
};
set<asd> 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<pair<ll,ll> >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<a.size();i++){
        k-=a[i].second;
        if(k<=0) return a[i].first;
    }
}
ll ksm(ll ds,ll zs,ll mod){
    ll now=ds%mod,ans=1;
    while(zs){
        if(zs&1) ans=ans*now%mod;
        now=now*now%mod;
        zs>>=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;
}

復雜度證明

傳送門

其它題目

\(CF343D\ Water\ Tree\)(珂朵莉樹上樹)

\(CF915E\ Physical\ Education\ Lessons\)(正解為動態開點線段樹)

\(P4979\) 礦洞:坍塌

\(P1558\) 色板游戲

\(P3740\) 貼海報

\(P5350\) 序列

\(P1204\) 擠牛奶 \(Milking\ Cows\)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM