珂朵莉树 ODT

提醒:这个只是作者复习用的,需要具体学习请转移其他 blog。

能干什么/局限性

高效处理区间平推(区间赋值)的问题。

在随机数据下飞快,而且看上去十分暴力。

如果没有区间平推,或者区间平推的操作数量可以被卡得很少甚至没有,就不适用。

前置知识

  • set

没了。

建点

每个点要维护一个区间,以及这个区间的信息。表示这个区间的信息都是相等的。

所有的点维护的区间都没有交集,且并集就是的区间就是 \(1\sim n\)

struct node{
	int l,r; //维护的区间
	mutable int val; //维护的信息
    //mutable 表示可以直接在 set 中修改
	node(int L,int R=-1,int VAL=0){
		l=L,r=R,val=VAL;
	}
	bool operator<(const node&other)const{
		return l<other.l;
	}
};
set<node> odt;

分裂(split)

传进去一个参数 \(pos\),表示把包含 \(pos\) 的区间 \([l,r]\),分裂成 \([l,pos-1]\)\([pos,r]\),并返回后一个区间的迭代器。

特别地,若 \(pos\) 所在的区间的左端点就是 \(pos\),那么不分裂直接返回这个区间。

auto split(int pos){
	auto it=odt.lower_bound(node(pos));
	if(it!=odt.end() && it->l==pos) return it; //特判
	it--;
	int l=it->l,r=it->r,val=it->val;
	odt.erase(it);
	odt.insert(node(l,pos-1,val)); //每个点维护的区间信息都是相同的
	return odt.insert(node(pos,r,val)).first;
}

平推(assgin)

如果只分裂那肯定是不行的,复杂度只会越来越高。

平推就是可以把一堆区间赋值成相同的,就可以合并起来。这也是 ODT 必须要有的平推操作,而且这也关乎到复杂度。

void assgin(int l,int r,int k){
	auto itr=split(r+1),itl=split(l);
	odt.erase(itl,itr);
	odt.insert(node(l,r,k));
}

所有的 ODT 的题必须要有的操作就这俩。

剩下的就全看实际题目了。

CF896C

发现操作 4 一般的数据结构还真做不了。

有平推操作,且数据随机,考虑 ODT。

区间加

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

查询第 K 小

int kth(int l,int r,int k){
	vector<pii> vec;
	auto itr=split(r+1),itl=split(l);
	for(auto it=itl;it!=itr;++it)
		vec.push_back({it->val,(it->r)-(it->l)+1});
	sort(vec.begin(),vec.end());
	for(pii r:vec){
		k-=r.second;
		if(k<=0) return r.first;
	}
	return -1;
}

查询区间幂次和

int pow(int a,int b,int p){
	int r=1; a%=p;
	while(b>0){
		if(b&1) r=r*a%p;
		b>>=1; a=a*a%p;
	}
	return r;
}
int sum(int l,int r,int x,int y){
	int ans=0;
	auto itr=split(r+1),itl=split(l);
	for(auto it=itl;it!=itr;++it)
		ans=(ans+pow(it->val,x,y)*((it->r)-(it->l)+1))%y;
	return ans;
}

为什么 ODT 可以解决这种查询?因为有多个连续的区间的数是相同的。

posted @ 2025-10-04 16:47  StarsIntoSea_SY  阅读(15)  评论(0)    收藏  举报