珂朵莉树

例题

简介

珂朵莉树,一种暴力的数据结构,一般用来写对拍或者是骗分,在随机数据的情况下可以 $ AC $,对于大部分操作,珂朵莉树都可以用 $ \log n $ 到 $ \log \log n$ 的时间处理(当然是均摊)。

珂朵莉树维护的是一段相同的区间,区间左右点和值。用 $ set $ 维护,按左端点排序。

struct node{
	int l,r;
	mutable ll v;
	//mutable极为重要,但是只需要记忆 影响set内值的改变
	node (int lll=0,int rr=0,ll vv=0) {
		l=lll;r=rr;v=vv;
	}//
	bool operator <(const node &i)const {
		return l<i.l;
	}
};
set <node> s;

操作split

用于拆分一个区间,区间的值可能改变,区间值的连续性就会发生破坏,这个时候就要把一个区间拆分成几个符合条件的区间。

//把区间[l,r]拆为[l,x-1]和[x,r]
IT split(int x){
	IT it=s.lower_bound(node(x,0,0));//找到第一个左端点大于等于 x 的点
	if(it!=s.end() && it->l==x) return it;//恰好就在l上就无须拆分
	it--;//找到[l,r]
	int L=it->l,R=it->r;ll V=it->v;
	s.erase(it);//删掉原本的区间改为两个
	s.insert(node(L,x-1,V));
	return s.insert(node(x,R,V)).first;.first是指针地址
}

操作assign

推平操作,也就是区间赋值,拆分一下区间,例如操作区间 $ [l,r] $,就变成 $ [l,x] $ 和 $ [x,y] $ 以及 $ [y,r] $,把这三个区间都赋为目标值即可。

void assign(int l,int r,ll x){
	IT itr=split(r+1),itl=split(l);//先右再左,否则r+1所在区间可能会被先删除,然后RE。
	s.erase(itl,itr);
	s.insert(node(l,r,x));
}

其余操作

都挺暴力的,都是暴力的取出去做。

void update(int l,int r,ll x){//区间加
	IT itr=split(r+1),itl=split(l);
	for(IT it=itl;it!=itr;it++)
		it->v+=x;
}
ll rankk(int l,int r,int k) {//区间k小
	vector<pll> p;
	IT itr=split(r+1),itl=split(l);
	p.clear();
	for(IT it=itl;it!=itr;it++)
		p.pb(make_pair(it->v,it->r - it->l + 1));
	sort(p.begin(),p.end());
	for(vector<pll >::iterator it=p.begin();it!=p.end();it++){
		k-= it->second;
		if(k<=0) return it->first;
	}
	return -inf;
}
ll pow_query(int l,int r,ll x,ll mod){//区间次幂和
	IT itr=split(r+1),itl=split(l);
	ll sum=0;
	for(IT it=itl;it!=itr;it++)
		sum = (sum + (ll)(it->r - it->l + 1) * pow(it->v, x, mod)) % mod;
	return sum;
}

所以这东西真的暴力。珂朵莉树的时间复杂度主要取决于他的推平操作,需要足够次数的推平操作才能维护 $ \log $ 的复杂度。所以卡掉一个珂朵莉树只需要删去推平操作......

posted @ 2023-08-17 21:28  point_fish  阅读(21)  评论(0)    收藏  举报