珂朵莉树/颜色段均摊学习笔记

珂朵莉树是基于set的数据结构,与线段树、平衡树等树形结构类似,珂朵莉树是用来解决区间问题的很暴力的树形结构

该数据结构支持区间推平,即将区间\([l,r]\)赋值为同一个值

1. 前置知识 set

set本质是平衡树,所以不会出现重复的元素且会自动排序,部分函数:

set <Node> t;            //定义一个名为t,类型为Node(结构体)的set
s.begin();               //返回指向第一个元素的迭代器(地址)
s.end();                 //返回指向最后一个元素的迭代器
s.clear();               //清空s
s.insert(a);             //将a插入到s
s.erase(l,r);            //将迭代器l~r之间的元素全部删除
s.lower_bound(a);        //二分查找a在s中的位置,返回一个迭代器。
set<Node>::iterator pos; //定义一个迭代器pos。

2. 实现

2.1. 构造

上文提到珂朵莉树用于解决区间推平操作,那么推平以后一段连续区间的值就是相同的

所以经过若干次推平后,我们可以看成这个序列上的数字是一段一段的,每一小段数字相同

此时,可以使用一个结构体存储数值相同的段

例如序列 1 1 1 1 1 2 2 2 2 3 3 3 3

在珂朵莉树上这个序列被表示为(1, 5, 1) (6, 9, 2) (10, 14, 3)三个节点

struct node{
	ll l,r; //区间左端点与右端点
	mutable ll v; //mutable为“可变的”,使我们可以直接修改v的值
	node(ll l,ll r=0,ll v=0): l(l),r(r),v(v) {}
	bool operator < (const node &x) const{
		return l<x.l;
	}
};//定义一个结构体,重载<为按左端点排序

2.2. 区间分割

进行修改和区间加时,可能就不能在原区间上操作,就需要把它分开

假设要将pos所在的节点以pos为中心分为两个节点,具体步骤如下:

  1. 二分找到pos所在节点\((l,r,val)\)

  2. 删除该节点

  3. 将这个节点分裂成\((l,pos-1,val)\)\((pos,r,val)\)扔回set

  4. 返回右区间迭代器

//以pos去做切割,找到一个包含pos的区间,把它分成[l,pos-1],[pos,r]两半
IT split(int pos)
{
	IT it=s.lower_bound(node(pos));
	if(it!=s.end()&&it->l==pos)
	{
		return it;
	}
	it--;
	if(it->r<pos) return s.end();
	ll l=it->l,r=it->r,v=it->v;
	s.erase(it);
	s.insert(node(l,pos-1,v));
	//insert函数返回pair,其中的first是新插入结点的迭代器
	return s.insert(node(pos,r,v)).first;
}

2.3. 区间推平

假设要推平区间\([l,r]\),就要先把它分裂出来,然后将这些区间删掉,然后添加一个新的段即可

void assign(ll l,ll r,ll x)
{
	IT itr=split(r+1),itl=split(l);
	s.erase(itl,itr);
	s.insert(node(l,r,x));
}

2.4. 区间加

先将操作区间分裂出来,然后暴力修改即可

void add(ll l,ll r,ll x)
{
	IT itr=split(r+1),itl=split(l);
	for(IT it=itl;it!=itr;++it)
	{
		it->v+=x;
	}
}

3. 例题

Willem, Chtholly and Seniorious

区间第k小可以将所在区间的段分裂出来,然后按值排序,当size大于等于k时直接输出

求x次方和就可以将所包含的每一段的单个值求出来,在乘个数求和即可

其他按上述求解即可

代码:

#include<bits/stdc++.h>
#define ll long long
#define IT set<node>::iterator
using namespace std;
ll n,m,seed,vmax,a[100005];
struct node{
	ll l,r;
	mutable ll v;
	node(ll l,ll r=0,ll v=0): l(l),r(r),v(v) {}
	bool operator < (const node &x) const{
		return l<x.l;
	}
};
set<node> s;
IT split(int pos)
{
	IT it=s.lower_bound(node(pos));
	if(it!=s.end()&&it->l==pos)
	{
		return it;
	}
	it--;
	if(it->r<pos) return s.end();
	ll 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;
}
void assign(ll l,ll r,ll x)
{
	IT itr=split(r+1),itl=split(l);
	s.erase(itl,itr);
	s.insert(node(l,r,x));
}
void add(ll l,ll r,ll x)
{
	IT itr=split(r+1),itl=split(l);
	for(IT i=itl;i!=itr;++i)
	{
		i->v+=x;
	}
}
struct rnk{
	ll num,cnt;
	bool operator < (const rnk &a)const{
		return num<a.num;
	}
	rnk(ll num,ll cnt): num(num),cnt(cnt) {}
};
ll getrank(ll l,ll r,ll x)
{
	IT itr=split(r+1),itl=split(l);
	vector<rnk> v;
	v.clear();
	for(IT i=itl;i!=itr;++i)
	{
		v.push_back(rnk(i->v,i->r-i->l+1));
	}
	sort(v.begin(),v.end());
	int i;
	for(i=0;i<v.size();i++)
	{
		if(v[i].cnt<x)
		{
			x-=v[i].cnt;
		}
		else break;
	}
	return v[i].num;
}
ll qpow(ll x,ll y,ll p)
{
	x%=p;
	ll res=1;
	while(y)
	{
		if(y&1) res=res*x%p;
		x=x*x%p;
		y>>=1;
	}
	return res;
}
ll getans(ll l,ll r,ll x,ll y)
{
	IT itr=split(r+1),itl=split(l);
	ll ans=0;
	for(IT i=itl;i!=itr;++i)
	{
		ans=(ans+qpow(i->v,x,y)*(i->r-i->l+1)%y)%y;
	}
	return ans;
}
ll rnd()
{
	ll ret=seed;
	seed=(seed*7+13)%1000000007;
	return ret;
}
int main()
{
	scanf("%lld%lld%lld%lld",&n,&m,&seed,&vmax);
	for(int i=1;i<=n;i++)
	{
		a[i]=(rnd()%vmax)+1;
	//	printf("%d ",a[i]);
		s.insert(node(i,i,a[i]));
	}
//	printf("1");
	for(int i=1;i<=m;i++)
	{
		ll op,l,r,x,y;
		op=(rnd()%4)+1;
		l=(rnd()%n)+1;
        r=(rnd()%n)+1;
        if(l>r) swap(l,r);
        if(op==3) x=(rnd()%(r-l+1))+1;
        else x=(rnd()%vmax)+1;
        if (op==4) y=(rnd()%vmax)+1;
     //   printf("%d %d %d %d\n",op,l,r,x);
        if(op==1)
        {
        	add(l,r,x);
		}
		if(op==2)
		{
			assign(l,r,x);
		}
		if(op==3)
		{
			printf("%lld\n",getrank(l,r,x));
		}
		if(op==4)
		{
			printf("%lld\n",getans(l,r,x,y));
		}
	}
	return 0;
}
posted @ 2024-08-21 18:00  wangsiqi2010916  阅读(22)  评论(0编辑  收藏  举报