珂朵莉树
珂朵莉树(或称ODT(Old Driver Tree老司机树)), 一般用来解决本来应当由线段树解决的区间类问题
核心思想
骗分暴力
适用条件
题目中必须有区间赋值操作
数据纯随机 保证时间复杂度
详解
珂朵莉树–初始化
一般珂朵莉树选择使用
s
e
t
set
set 维护数列
s
e
t
set
set中每个结点维护三个值
(
l
l
,
r
r
,
v
a
l
)
( ll , rr , val )
(ll,rr,val),意为
[
l
l
,
r
r
]
[ ll , rr ]
[ll,rr]内的值都为
v
a
l
val
val 也就是每个节点保存一段连续的值相同的区间
set中以每个区间左端点排序
struct node
{
int ll,rr;
mutable int val;
node(int L,int R=-1,int V=0): ll(L), rr(R), val(V) {}
bool operator < (const node& tt)const { return ll<tt.ll;}//以区间左端点排序
};
set<node> st;
需要注意的是
m
u
t
a
b
l
e
mutable
mutable
没有它对
v
a
l
val
val的修饰,在接下来
a
d
d
add
add函数里导致CE。
CE的原因 涉及对val的修改, 但是该node用const修饰, 为突破const需要对
v
a
l
val
val的修饰
接下来为方便有关迭代器的操作,我们做一个宏定义
#define IT set<node>::iterator
珂朵莉树–分裂Split
split(pos)操作是指将原来含有pos位置的节点分成两部分
[
l
,
p
o
s
−
1
]
[l,pos-1]
[l,pos−1]和
[
p
o
s
,
r
]
[pos,r]
[pos,r]
并返回分裂后以
p
o
s
pos
pos为左端点的结点的迭代器
IT split(int pos){
//二分找到第一个左端点不小于pos的区间
IT it = st.lower_bound(node(pos));
//pos本身就是某个区间的左端点,不用分裂
if(it != st.end()&&it->ll == pos){
return it;
}
//否则上一个区间才是包含pos的区间
--it;
int ll = it->ll, rr = it->rr, val = it->val;
//删除原结点
st.erase(it);
st.insert(node(ll, pos-1, val));
return st.insert(node(pos, rr, val)).first;
}
珂朵莉树–区间赋值Assign
珂朵莉树就是靠这个东西维持其不正确的复杂度的
void assign(int ll, int rr, int val){
//分裂出需要的区间 注意分裂出[ll ,rr]区间时要先分裂右端点,再分裂左端点
IT itr = split(rr+1), itl = split(ll);
//删除旧区间 erase方法可以删除迭代器描述的区间[first,last),注意左闭右开
st.earse(itl, itr);
//插入新区间
st.insert(node(ll, rr, val));
}
在数据纯随机的情况下,可以证明每次assign的区间长度期望为N/3
于是set规模迅速下降,随后达到接近O(mlogn)的玄学非正确复杂度
珂朵莉树–其他暴力操作
其他操作真的就是规规矩矩的纯暴力
直接取出对应区间,暴力对每一个进行操作
一般就是长这样
void fun(int ll,int rr){
IT itr = split(rr + 1),itl = split(ll);
for(;itl != itr;++itl) {
//...
}
}
区间元素增加val
void add(int ll,int rr,lt val){
IT itr=split(rr+1),itl=split(ll);
for(;itl!=itr;++itl) itl->val+=val;
}
区间求和
int qsum(int ll, int rr){
int res = 0;
IT itr = split(rr + 1), itl = split(ll);
for(; itl != itr; ++itl){
res += (itl->rr - itl->ll + 1){
*itl->val;
}
}
return res;
}
区间第K位
#define PII pair<int, int>
int kth(int ll, int rr, int k){
vector<PII> vec;
IT itr = split(rr + 1), itl = split(ll);
for(; itl != itr; ++itl){
vec.push_back(PII(itl->val, itl->rr-itl->ll+1));
//全部存下来排序就好
sort(vec.begin(), vec.end());
}
for(vector<PII>::iterator it = vec.begin(); it != vec.end(); ++it){
k -= it->second;
if(k <= 0){
return it->first;
}
}
return -1;
}
珂朵莉树–暴力AC应用
CodeForces - 896C Willem, Chtholly and Seniorious
毒瘤ODT的源头
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cstring>
#include<cstdio>
#include<set>
using namespace std;
typedef long long lt;
#define IT set<node>::iterator
#define pir pair<lt,int>
#define mkp(x,y) make_pair(x,y)
lt read()
{
lt f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
lt seed,vmax;
lt rnd()
{
lt res=seed;
seed=(seed*7+13)%1000000007;
return res;
}
const int maxn=100010;
int n,m;
lt a[maxn];
struct node
{
int ll,rr;
mutable lt val;
node(int L,int R=-1,lt V=0): ll(L), rr(R), val(V) {}
bool operator < (const node& tt)const { return ll<tt.ll;}
};
set<node> st;
lt qpow(lt a,lt k,lt p)
{
lt res=1; a%=p;
while(k>0){
if(k&1) res=(res*a)%p;
a=(a*a)%p; k>>=1;
}
return res;
}
IT split(int pos)
{
IT it=st.lower_bound(node(pos));
if(it!=st.end()&&it->ll==pos) return it;
--it;
int ll=it->ll,rr=it->rr;
lt val=it->val;
st.erase(it);
st.insert(node(ll,pos-1,val));
return st.insert(node(pos,rr,val)).first;
}
void assign(int ll,int rr,lt val)
{
IT itr=split(rr+1),itl=split(ll);
st.erase(itl,itr);
st.insert(node(ll,rr,val));
}
void add(int ll,int rr,lt val)
{
IT itr=split(rr+1),itl=split(ll);
for(;itl!=itr;++itl) itl->val+=val;
}
lt kth(int ll,int rr,int k)
{
vector<pir> vec;
IT itr=split(rr+1),itl=split(ll);
for(;itl!=itr;++itl)
vec.push_back(pir(itl->val,itl->rr-itl->ll+1));
sort(vec.begin(),vec.end());
for(vector<pir>::iterator it=vec.begin();it!=vec.end();++it)
{
k-=it->second;
if(k<=0) return it->first;
}
return -1;
}
lt qsum(int ll,int rr,lt x,lt y)
{
lt res=0;
IT itr=split(rr+1),itl=split(ll);
for(;itl!=itr;++itl)
res+=(qpow(itl->val,x,y)*((itl->rr-itl->ll+1)%y))%y,res%=y;
return res;
}
int main()
{
n=read();m=read();
seed=read();vmax=read();
for(int i=1;i<=n;++i)
{
a[i]=(rnd()%vmax)+1;
st.insert(node(i,i,a[i]));
}
for(int i=1;i<=m;++i)
{
int op=(rnd()%4)+1;
int ll=(rnd()%n)+1,rr=(rnd()%n)+1;
lt x,y;
if(ll>rr) swap(ll,rr);
if(op==3) x=(rnd()%(rr-ll+1))+1;
else x=(rnd()%vmax)+1;
if(op==4) y=(rnd()%vmax)+1;
if(op==1) add(ll,rr,x);
else if(op==2) assign(ll,rr,x);
else if(op==3) printf("%lld\n",kth(ll,rr,x));
else if(op==4) printf("%lld\n",qsum(ll,rr,x,y));
}
return 0;
}
主要借鉴该博客
原创不易
转载请标明出处
如果对你有所帮助 别忘啦点赞支持哈