这种暴力又可爱的数据结构真的相当讨喜。如果题目中有操作是推平一段区间的话,那么它多半就是一道 ODT 的题。如果题目还保证了数据随机的话,那么恭喜你,ODT 一定可以简单高效的解决这个问题;但如果出题人卡数据的话,ODT 一定会 T 的飞起。
掌握 ODT 的难度是相当小的,只要理解到 split 和 assign 就好了,这两种操作也是相当简单。其他附加的操作也是在这两种操作的基础上怎么暴力怎么来。
两道板子题
CF896C
传送们
万恶之源,也是珂朵莉树的名字来源。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <set>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef pair<LL,int> PLI;
const int N=1e5+10;
const int mod=1e9+7;
int n,m;
LL seed,vmax,a[N];
LL qpow(LL x,LL k,LL mod){
LL res=1;x%=mod;
while(k){
if(k&1) res=res*x%mod;
x=x*x%mod;k>>=1;
}
return res;
}
struct ChthollyTree{
#define setit set<Node>::iterator
struct Node{
int l,r;
mutable LL x;
friend bool operator < (Node a,Node b){return a.l<b.l;}
};
set<Node> st;
void build(){for(int i=1;i<=n+1;i++) st.insert(Node{i,i,a[i]});}
setit split(int pos){
setit it=st.lower_bound(Node{pos});
if(it!=st.end()&&it->l==pos) return it;
it--;
int l=it->l,r=it->r;LL x=it->x;
st.erase(it);
st.insert(Node{l,pos-1,x});
return st.insert(Node{pos,r,x}).first;
}
void assign(int l,int r,LL x){
setit itl=split(l),itr=split(r+1);
st.erase(itl,itr);
st.insert(Node{l,r,x});
}
void add(int l,int r,LL v){
setit itl=split(l),itr=split(r+1);
for(setit it=itl;it!=itr;it++) it->x+=v;
}
LL getnum(int l,int r,int k){
setit itl=split(l),itr=split(r+1);
vector<pair<LL,int> > vp;
for(setit it=itl;it!=itr;it++) vp.push_back({it->x,it->r-it->l+1});
sort(vp.begin(),vp.end());
for(pair<LL,int> p:vp){
k-=p.second;
if(k<=0) return p.first;
}
return -1;
}
LL asksum(int l,int r,int k,int mod){
setit itl=split(l),itr=split(r+1);
LL ans=0;
for(setit it=itl;it!=itr;it++)
ans=( ans+qpow(it->x,k,mod)*(it->r-it->l+1) )%mod;
return ans;
}
}odt;
int rnd(){
int ret=seed;
seed=(seed*7+13)%mod;
return ret;
}
int main(){
cin>>n>>m>>seed>>vmax;
for(int i=1;i<=n;i++) a[i]=rnd()%vmax+1;
odt.build();
while(m--){
int opt=rnd()%4+1,l=rnd()%n+1,r=rnd()%n+1,x,y;
if(l>r) swap(l,r);
if(opt==3) x=rnd()%(r-l+1)+1;
else x=rnd()%vmax+1;
if(opt==4) y=rnd()%vmax+1;
if(opt==1) odt.add(l,r,x);
else if(opt==2) odt.assign(l,r,x);
else if(opt==3) printf("%lld
",odt.getnum(l,r,x));
else if(opt==4) printf("%lld
",odt.asksum(l,r,x,y));
}
return 0;
}
2019太原理工新生预赛L题
传送门
稍微简单一些的典型的珂朵莉树的应用
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <set>
#include <queue>
using namespace std;
typedef long long LL;
const int N=1e5+10;
int n,m;
struct ChthollyTree{
#define setit set<Node>::iterator
struct Node{
int l,r;
mutable LL val;
friend bool operator < (Node a,Node b){return a.l<b.l;}
};
set<Node> s;
void build(){s.insert(Node{1,n,1});s.insert(Node{n+1,n+1,0});}
setit split(int pos){
setit it=s.lower_bound(Node{pos});
if(it!=s.end()&&it->l==pos) return it;
it--;
int l=it->l,r=it->r,val=it->val;
s.erase(it);
s.insert(Node{l,pos-1,val});
return s.insert(Node{pos,r,val}).first;
}
void assign(int l,int r,int val){
setit itl=split(l),itr=split(r+1);
s.erase(itl,itr);
s.insert(Node{l,r,val});
}
void sumtor(int l,int r){
setit itl=split(l),itr=split(r+1);
LL sum=0;
for(setit it=itl;it!=itr;it++) sum+=it->val*(it->r-it->l+1);
assign(l,r-1,0);
assign(r,r,sum);
}
int count(int l,int r){
setit itl=split(l),itr=split(r+1);
set<LL> st;
int ans=0;
for(setit it=itl;it!=itr;it++) if(it->val) st.insert(it->val);
return st.size();
}
}odt;
inline 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*10+c-'0';c=getchar();}
return x*f;
}
int main(){
n=read();m=read();
odt.build();
for(int i=1,opt,l,r;i<=m;i++){
opt=read(),l=read(),r=read();
if(opt==1) odt.assign(l,r,1);
else if(opt==2) odt.sumtor(l,r);
else if(opt==3) printf("%d
",odt.count(l,r));
}
return 0;
}