珂朵莉镇楼qwq
算法基础
这个算法的基础很神奇,大家想一下这样一个问题:
有一个长度为 n n n 的序列,随机选出一个区间,这个区间的期望长度是?
(要是会的话就跳过做法吧)
做法
考虑左端点在 1 1 1 位置的情况,那么区间期望长度为: 1 + n 2 \frac {1+n} 2 21+n,这种情况的出现频率为 n n n。
考虑左端点在 2 2 2 位置的情况,那么区间期望长度为: n 2 \frac n 2 2n,这种情况的出现概率为 n − 1 n-1 n−1。
……
那么最终的期望就是
(
1
+
n
)
×
n
2
+
n
×
(
n
−
1
)
2
+
(
n
−
1
)
×
(
n
−
2
)
2
.
.
.
+
(
1
+
1
)
×
1
2
n
(
n
+
1
)
2
=
(
n
+
1
)
n
+
n
(
n
−
1
)
+
(
n
−
1
)
(
n
−
2
)
+
.
.
.
+
2
×
1
n
(
n
+
1
)
=
1
×
2
+
2
×
3
+
3
×
4
+
.
.
.
n
(
n
+
1
)
=
(
1
2
+
2
2
+
3
2
+
.
.
.
+
n
2
)
+
(
1
+
2
+
3
+
.
.
.
+
n
)
n
(
n
+
1
)
=
n
(
n
+
1
)
(
2
n
+
1
)
/
6
+
n
(
n
+
1
)
/
2
n
(
n
+
1
)
=
(
2
n
+
1
)
/
6
+
1
/
2
=
1
3
n
+
1
6
+
1
2
\begin{aligned} &\frac {\frac {(1+n) \times n} 2 + \frac {n \times (n-1)} 2 + \frac {(n-1) \times (n-2)} 2...+\frac {(1+1)\times 1} 2} {\frac {n(n+1)} 2}\\ &=\frac {(n+1)n+n(n-1)+(n-1)(n-2)+...+2\times 1} {n(n+1)}\\ &=\frac {1\times 2 +2\times 3 + 3\times 4 +...} {n(n+1)}\\ &=\frac {(1^2+2^2+3^2+...+n^2)+(1+2+3+...+n)} {n(n+1)}\\ &=\frac {n(n+1)(2n+1)/6+n(n+1)/2} {n(n+1)}\\ &=(2n+1)/6+1/2\\ &=\frac 1 3 n +\frac 1 6+\frac 1 2 \end{aligned}
2n(n+1)2(1+n)×n+2n×(n−1)+2(n−1)×(n−2)...+2(1+1)×1=n(n+1)(n+1)n+n(n−1)+(n−1)(n−2)+...+2×1=n(n+1)1×2+2×3+3×4+...=n(n+1)(12+22+32+...+n2)+(1+2+3+...+n)=n(n+1)n(n+1)(2n+1)/6+n(n+1)/2=(2n+1)/6+1/2=31n+61+21
舍去后面两个跟没有一样的分数,可以得出,随机选出一个区间,这个区间的期望长度为
1
3
n
\frac 1 3 n
31n。
这便是珂朵莉树的基础。
正题
使用前提
使用珂朵莉树的前提有两个:
- 数据随机(这样上面那个性质就可以用了!)
- 有
将某个区间都修改为某个值
这样的操作
核心思想
将序列中值相同的一段区间压成一个点。
比如说,有一个序列 1 1 1 1 1 1 2 2 3 3 3 3 1~1~1~1~1~1~2~2~3~3~3~3 1 1 1 1 1 1 2 2 3 3 3 3
那么会压成三个点,这三个点是: ( 1 , 6 , 1 ) , ( 7 , 8 , 2 ) , ( 9 , 12 , 3 ) (1,6,1)~,~(7,8,2)~,~(9,12,3) (1,6,1) , (7,8,2) , (9,12,3)。
其中 ( a , b , c ) (a,b,c) (a,b,c) 表示序列中区间 [ a , b ] [a,b] [a,b] 的值都是 c c c。
然后这些点我们用 s e t set set 维护一下就好了。
因为有将某个区间都修改为某个值
这样的操作,并且数据随机,那么我们每一次就可以将整个序列中长度为
1
3
n
\frac 1 3 n
31n 的区间改成一个相同的值,然后我们可以将这个区间压成一个点。
然后因为操作种数不是很多,以模板题为例,只有 4 4 4 种操作,那么在随机情况下,每 4 4 4 个操作里面就有一个区间修改操作,那么这就注定了这个序列不会被压成太多点,点数大约是 l o g ( n ) log(n) log(n) 个的,然后算上 s e t set set 的时间复杂度,那么总的时间复杂度就是 O ( n l o g ( n ) l o g ( l o g ( n ) ) ) O(nlog(n)log(log(n))) O(nlog(n)log(log(n)))。
先贴出点
的结构体代码:
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
s p l i t ( p o s ) split(pos) split(pos) 这样的操作是将包含 p o s pos pos 这个位置的点拆成分别管理 [ l , p o s − 1 ] [l,pos-1] [l,pos−1] 和 [ p o s , r ] [pos,r] [pos,r] 的两个点,并且返回管理 [ p o s , r ] [pos,r] [pos,r] 的这个点。这个返回值后面有大用处。
代码如下:
//完整代码中已经将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));//压成一个点加回进去
}
s e t set set 是一个相当方便的东西,里面这个 e r a s e erase erase 函数就有不少用法,上面用到的是 e r a s e ( f i r s t , l a s t ) erase(first,last) erase(first,last),他会帮你删除 s e t set set 中的区间 [ f i r s t , l a s t ) [first,last) [first,last)。
核心操作讲完了,接下来顺便讲讲模板题的剩下两个操作。
求区间第k小
由于点数大约是 l o g ( n ) log(n) log(n) 级别的,所以我们直接把这个区间掏出来暴力排序找就是了。
代码如下:
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;//减没了说明就是这个数
}
}
求区间 x x x 次方对 y y y 取模的值
依然是暴力乱做:
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;
}
最后是模板题的 A C AC AC 代码:
#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));
}
}
}