珂朵莉镇楼qwq
算法基础
这个算法的基础很神奇,大家想一下这样一个问题:
有一个长度为 的序列,随机选出一个区间,这个区间的期望长度是?
(要是会的话就跳过做法吧)
做法
考虑左端点在 位置的情况,那么区间期望长度为: ,这种情况的出现频率为 。
考虑左端点在 位置的情况,那么区间期望长度为: ,这种情况的出现概率为 。
……
那么最终的期望就是
舍去后面两个跟没有一样的分数,可以得出,随机选出一个区间,这个区间的期望长度为
。
这便是珂朵莉树的基础。
正题
使用前提
使用珂朵莉树的前提有两个:
- 数据随机(这样上面那个性质就可以用了!)
- 有
将某个区间都修改为某个值
这样的操作
核心思想
将序列中值相同的一段区间压成一个点。
比如说,有一个序列
那么会压成三个点,这三个点是: 。
其中 表示序列中区间 的值都是 。
然后这些点我们用 维护一下就好了。
因为有将某个区间都修改为某个值
这样的操作,并且数据随机,那么我们每一次就可以将整个序列中长度为
的区间改成一个相同的值,然后我们可以将这个区间压成一个点。
然后因为操作种数不是很多,以模板题为例,只有 种操作,那么在随机情况下,每 个操作里面就有一个区间修改操作,那么这就注定了这个序列不会被压成太多点,点数大约是 个的,然后算上 的时间复杂度,那么总的时间复杂度就是 。
先贴出点
的结构体代码:
struct node{
int l,r;
mutable ll val;
//这个mutable是为了保证我们可以在set里面修改它
node(int L,int R=0,ll v=0):l(L),r(R),val(v){};
bool operator <(const node b)const{return l<b.l;}//用来给set排序
};
核心操作
split
这样的操作是将包含 这个位置的点拆成分别管理 和 的两个点,并且返回管理 的这个点。这个返回值后面有大用处。
代码如下:
//完整代码中已经将it这个东西define成set<node>::iterator了
it split(int pos)
{
it p=f.lower_bound(node(pos));//找到第一个左端点大于等于pos的点
if(p!=f.end()&&p->l==pos)return p;//假如找到的这个点刚好以pos作为左端点,那么就不用拆了
p--;//否则将p--,找到上一块,这一块一定包含pos
int l=p->l,r=p->r;ll val=p->val;//记录下这一块的信息
f.erase(p);//删掉这一块
f.insert(node(l,pos-1,val));//分成两块加回去
return f.insert(node(pos,r,val)).first;
//set的insert函数是有返回值的,会返回一个pair,其中first是插入的值在set中的指针,second是 是否插入成功
}
assign
这是区间推平操作,也就是上面的将某个区间都修改为某个值
。
代码如下:
void assign(int l,int r,int val)
{
it x=split(l),y=split(r+1);
//分裂出l,r位置,注意这里的y是指向包含r+1这个位置的,而不是包含r这个位置的
f.erase(x,y);//将管理[l,r]这一个区间的所有点删除,下面会讲这个函数
f.insert(node(l,r,val));//压成一个点加回进去
}
是一个相当方便的东西,里面这个 函数就有不少用法,上面用到的是 ,他会帮你删除 中的区间 。
核心操作讲完了,接下来顺便讲讲模板题的剩下两个操作。
求区间第k小
由于点数大约是 级别的,所以我们直接把这个区间掏出来暴力排序找就是了。
代码如下:
struct point{
ll x;int y;//表示x这个值出现过y次
point(ll xx,int yy):x(xx),y(yy){}
bool operator <(const point b)const{return x<b.x;}
};
ll findkth(int x,int y,int k)
{
it l=split(x),r=split(y+1);//依然要先分离出这个区间
vector<point>vec;
for(;l!=r;l++)//遍历这个区间,用vector存起来
vec.push_back(point(l->val,l->r-l->l+1));
sort(vec.begin(),vec.end());//排序
for(int i=0;i<vec.size();i++)
{
k-=vec[i].y;//减去出现次数
if(k<=0)return vec[i].x;//减没了说明就是这个数
}
}
求区间 次方对 取模的值
依然是暴力乱做:
ll ksm(ll x,int y,int mod)
{
ll re=1,tot=x%mod;
while(y)
{
if(y&1)re=re*tot%mod;
tot=tot*tot%mod;
y>>=1;
}
return re;
}
ll ask(int x,int y,int p,int mod)
{
ll ans=0;
it l=split(x),r=split(y+1);//分离
for(;l!=r;l++)//遍历
ans=(ans+(ll)(l->r-l->l+1)%mod*ksm(l->val,p,mod))%mod;//求解
return ans;
}
最后是模板题的 代码:
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <algorithm>
using namespace std;
#define ll long long
#define it set<node>::iterator
int n,m,vmax;
ll seed;
int rnd()
{
int re=seed;
seed=(seed*7ll+13ll)%1000000007;
return re;
}
struct node{
int l,r;
mutable ll val;
node(int L,int R=0,ll v=0):l(L),r(R),val(v){};
bool operator <(const node b)const{return l<b.l;}
};
set <node>f;
it split(int pos)
{
it p=f.lower_bound(node(pos));
if(p!=f.end()&&p->l==pos)return p;
p--;
int l=p->l,r=p->r;ll val=p->val;
f.erase(p);
f.insert(node(l,pos-1,val));
return f.insert(node(pos,r,val)).first;
}
void assign(int l,int r,int val)
{
it x=split(l),y=split(r+1);
f.erase(x,y);
f.insert(node(l,r,val));
}
void add(int l,int r,int val)
{
it x=split(l),y=split(r+1);
for(;x!=y;x++)x->val+=val;
}
struct point{
ll x;int y;
point(ll xx,int yy):x(xx),y(yy){}
bool operator <(const point b)const{return x<b.x;}
};
ll findkth(int x,int y,int k)
{
it l=split(x),r=split(y+1);
vector<point>vec;
for(;l!=r;l++)
vec.push_back(point(l->val,l->r-l->l+1));
sort(vec.begin(),vec.end());
for(int i=0;i<vec.size();i++)
{
k-=vec[i].y;
if(k<=0)return vec[i].x;
}
}
ll ksm(ll x,int y,int mod)
{
ll re=1,tot=x%mod;
while(y)
{
if(y&1)re=re*tot%mod;
tot=tot*tot%mod;
y>>=1;
}
return re;
}
ll ask(int x,int y,int p,int mod)
{
ll ans=0;
it l=split(x),r=split(y+1);
for(;l!=r;l++)
ans=(ans+(ll)(l->r-l->l+1)%mod*ksm(l->val,p,mod))%mod;
return ans;
}
int main()
{
scanf("%d %d %lld %d",&n,&m,&seed,&vmax);
for(int i=1,x;i<=n;i++)
f.insert(node(i,i,rnd()%vmax+1));
f.insert(node(n+1,n+1,0));//处理边界
for(int i=1,op,l,r,x,y;i<=m;i++)
{
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;
printf("%lld\n",findkth(l,r,x));
}
else
{
x=rnd()%vmax+1;
if(op==1)add(l,r,x);
if(op==2)assign(l,r,x);
}
if(op==4)
{
y=rnd()%vmax+1;
printf("%lld\n",ask(l,r,x,y));
}
}
}