珂朵莉树(ODT)学习笔记

珂朵莉树,一种以 \(set\) 为基础,支持区间推平等多种操作的暴力数据结构

复杂度的保证需要数据随机,为 \(O(m\log n)\)

主要思想:把颜色相同的区间合并在一起 其他操作只要把头和尾拆开暴力求就行

虽然看起来复杂度是假的 但在随机数据下跑的飞快

建树

struct node{
    int l,r;
    mutable int v;
    friend bool operator<(node a,node b){return a.l<b.l;}
};
set<node>s;

每个节点维护每个颜色段的开头结尾和颜色 重载运算符方便 \(set\) 排序

mutable 可以让后续操作对 \(set\)\(v\) 的值进行修改

区间切割

inl auto split(int pos){
    if(pos>n)return s.end();
    auto it=s.lower_bound((node){pos,0,0});
    if(it!=s.end()&&it->l==pos)return it;
    it--;int l=it->l,r=it->r,v=it->v;
    s.erase(it);
    s.insert((node){l,pos-1,v});
    return s.insert((node){pos,r,v}).first;
}

split操作需要返回开头指向 \(pos\) 位置的迭代器 方便后续区间操作

如果 \(pos\) 特别大 返回结尾即可

\(lower\text{_}bound\) 找到第一个区间开头 \(\ge pos\) 的位置

如果发现开头本来就等于 \(pos\) 那不用切了直接返回

否则把前一个切开

\(set\)\(insert\) 操作会返回一个 \(pair\) 为 指向插入元素的迭代器( \(set<node>::iterator\) ) 和 是否插入成功( \(bool\) ))

区间推平

ODT保证复杂度的核心操作

inl void assign(int l,int r,int x){
    auto itr=split(r+1),itl=split(l);
    s.erase(itl,itr);
    s.insert((node){l,r,x});
}

感觉很显然 把 \(l\)\(r\) 所处的块切开 然后全删了 整块加进去

注意先切后边 否则可能出现 \(r+1\)\(l\) 所在块切开的情况

其他操作

区间加

inl void add(int l,int r,int x){
    auto itr=split(r+1),itl=split(l);
    for(auto it=itl;it!=itr;it++)
        it->v+=x;
}

区间第 \(k\)

inl int rnk(int l,int r,int x){
    auto itr=split(r+1),itl=split(l);
    vector<node>v;
    for(auto it=itl;it!=itr;it++)v.push_back(*it);
    sort(v.begin(),v.end(),cmp);
    for(auto i:v){
        x-=(i.r-i.l+1);
        if(x<=0)return i.v;
    }
    return inf;
}

区间 \(k\) 次方和

inl int query(int l,int r,int x,int y){
    auto itr=split(r+1),itl=split(l);
    int ans=0;
    for(auto it=itl;it!=itr;it++)
        ans=(ans+(it->r-it->l+1)*qpow(it->v%y,x,y)%y)%y;
    return ans;
}

实际上就是暴力

CF896C Willem, Chtholly and Seniorious

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define inl inline
#define int ll
const int N=1e5+5;
const int M=2e3+5;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
inl int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*f;
}
inl void write(int x){
    if(x<0){x=-x;putchar('-');}
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
inl void writei(int x){write(x);putchar(' ');}
inl void writel(int x){write(x);putchar('\n');}
int n,m,seed,vmax,a[N],op,l,r,x,y;
struct node{
    int l,r;
    mutable int v;
    friend bool operator<(node a,node b){return a.l<b.l;}
};
set<node>s;
inl int rnd(){int sd=seed;seed=(seed*7+13)%mod;return sd;}
inl int qpow(int a,int b,int mod){
    int res=1;
    while(b){
        if(b&1)res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
inl auto split(int pos){
    if(pos>n)return s.end();
    auto it=s.lower_bound((node){pos,0,0});
    if(it!=s.end()&&it->l==pos)return it;
    it--;int l=it->l,r=it->r,v=it->v;
    s.erase(it);
    s.insert((node){l,pos-1,v});
    return s.insert((node){pos,r,v}).first;
}
inl void add(int l,int r,int x){
    auto itr=split(r+1),itl=split(l);
    for(auto it=itl;it!=itr;it++)
        it->v+=x;
}
inl void assign(int l,int r,int x){
    auto itr=split(r+1),itl=split(l);
    s.erase(itl,itr);
    s.insert((node){l,r,x});
}
inl bool cmp(node a,node b){return a.v<b.v;}
inl int rnk(int l,int r,int x){
    auto itr=split(r+1),itl=split(l);
    vector<node>v;
    for(auto it=itl;it!=itr;it++)v.push_back(*it);
    sort(v.begin(),v.end(),cmp);
    for(auto i:v){
        x-=(i.r-i.l+1);
        if(x<=0)return i.v;
    }
    return inf;
}
inl int query(int l,int r,int x,int y){
    auto itr=split(r+1),itl=split(l);
    int ans=0;
    for(auto it=itl;it!=itr;it++)
        ans=(ans+(it->r-it->l+1)*qpow(it->v%y,x,y)%y)%y;
    return ans;
}
signed main(){
    n=read();m=read();seed=read();vmax=read();
    for(int i=1;i<=n;i++)a[i]=(rnd()%vmax)+1;
    for(int i=1;i<=n;i++)s.insert((node){i,i,a[i]});
    for(int i=1;i<=m;i++){
        op=(rnd()%4)+1;
        l=(rnd()%n)+1;
        r=(rnd()%n)+1;
        if(l>r)swap(l,r);
        switch(op){
        case 1:
            x=(rnd()%vmax)+1;
            add(l,r,x);
            break;
        case 2:
            x=(rnd()%vmax)+1;
            assign(l,r,x);
            break;
        case 3:
            x=(rnd()%(r-l+1))+1;
            writel(rnk(l,r,x));
            break;
        default:
            x=(rnd()%vmax)+1;
            y=(rnd()%vmax)+1;
            writel(query(l,r,x,y));
            break;
        }
    }
    return 0;
}

CF915E Physical Education Lessons

这道题的线段树做法十分显然 于是我们考虑怎么不用线段树做这道题()

发现操作全是区间推平 那么ODT复杂度正确

每次修改动态维护答案即可

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define inl inline
#define ll long long
const int N=1e5+5;
const int M=1e6+5;
const int inf=0x7fffffff;
const int mod=1e9+7;
inl int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*f;
}
inl void write(int x){
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
inl void writei(int x){write(x);putchar(' ');}
inl void writel(int x){write(x);putchar('\n');}
int n,q,ans,l,r,k;
struct node{
    int l,r;
    mutable int v;
    friend bool operator<(node a,node b){return a.l<b.l;}
};
set<node>s;
inl auto split(int pos){
    if(pos>n)return s.end();
    auto it=s.lower_bound((node){pos,0,0});
    if(it!=s.end()&&it->l==pos)return it;
    it--;int l=it->l,r=it->r,v=it->v;
    s.erase(it);
    s.insert((node){l,pos-1,v});
    return s.insert((node){pos,r,v}).first;
}
inl void assign(int l,int r,int v){
    auto itr=split(r+1),itl=split(l);
    for(auto it=itl;it!=itr;it++)
        if(it->v^v)ans+=v?(it->r-it->l+1):-(it->r-it->l+1);
    s.erase(itl,itr);
    s.insert((node){l,r,v});
}
signed main(){
    n=read();q=read();ans=n;
    s.insert((node){1,n,1});
    while(q--){
        l=read();r=read();k=read();
        assign(l,r,k-1);
        writel(ans);
    }
    return 0;
}

posted @ 2023-11-06 15:30  xiang_xiang  阅读(49)  评论(0)    收藏  举报