珂朵莉树(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;
}

浙公网安备 33010602011771号