【洛谷P5586】[P5350] 序列 (加强版)
可持久化平衡树
题目大意:
给定一个序列,需要支持以下操作:
- 给定 $l,r$,问 $a_l\sim a_r$ 的和。
- 给定 $l,r,k$,将 $a_l\sim a_r$ 都变为 $k$。
- 给定 $l,r,k$,将 $a_l\sim a_r$ 都加上 $k$。
- 给定 $l_1,r_1,l_2,r_2$,保证 $r_1-l_1=r_2-l_2$ 且 $[l_1,r_1]\bigcap[l_2,r_2]=\varnothing$,将 $a_{l_2}\sim a_{r_2}$ 对应的位置变成 $a_{l_1}\sim a_{r_1}$ 的对应位置。
- 给定 $l_1,r_1,l_2,r_2$,保证 $r_1-l_1=r_2-l_2$ 且 $[l_1,r_1]\bigcap[l_2,r_2]=\varnothing$,交换 $a_{l_1}\sim a_{r_1}$ 和 $a_{l_2}\sim a_{r_2}$ 。
- 给定 $l,r$,翻转 $a_l\sim a_r$。
强制在线,且数据不随机。
解题思路:
P5350 是数据随机,所以可以直接珂朵莉树。
本题数据不随机。考虑使用不依赖随机数据特性的数据结构求解。
对于操作 $1,2,3,6$,属于数据结构基本操作,可以使用平衡树解决。
对于操作 $5$,不难想到用支持分裂合并的平衡树,将两个区间分裂出来,交换位置后合并即可。
对于操作 $4$,我们如何在正确的复杂度下实现复制一段区间的操作?
可持久化可以满足要求。我们把整棵平衡树可持久化,执行操作 $4$ 的时候,把区间分裂出来,然后把需要复制的结点克隆一份,再合并上去即可。
我使用可持久化 Treap 实现。每个操作的期望复杂度都是 $O(\log n)$ 的。
有一些注意事项:
- 直接对每个结点赋一个随机值作为 $key$ 的做法复杂度会有问题,正确的做法是在合并的时候按照子树大小来随机合并。
- 可持久化时会消耗非常多的结点。当开不下的时候,需要定期对树进行重构。由于我们已经不需要给每个结点赋一个随机值了,所以可以直接按照线段树的建树方式 $O(n)$ 完成建树而不需要笛卡尔树。
- 可能会有些卡常……
总时间复杂度 $O(m\log n)$。
Code:
#include<cstdio>
#include<ctime>
#include<cctype>
#include<random>
#include<vector>
#include<algorithm>
using namespace std;
const int N=3e5+6,md=1e9+7;
typedef long long LL;
mt19937 mt(time(0));
inline void upd(int&a){a+=a>>31&md;}
char buf[(int)1e8],*ss=buf;
inline int InIt(){buf[fread(buf,1,(int)1e8-1,stdin)]='\n';return 0;}
const int __START__=InIt();
inline int readint(){
int d=0;
while(!isdigit(*ss))++ss;
while(isdigit(*ss))d=d*10+(*ss++^'0');
return d;
}
namespace T{
const int M=8e6+6;
int val[M],d[M],ls[M],rs[M],sz[M],cov[M],add[M];bool tag[M];
int id,n,rt;
inline int newnode(int a){
d[++id]=a,val[id]=a;ls[id]=rs[id]=add[id]=0,sz[id]=1,tag[id]=0;
cov[id]=-1;
return id;
}
inline int clone(int x){
++id;
val[id]=val[x],d[id]=d[x],ls[id]=ls[x],rs[id]=rs[x],sz[id]=sz[x],cov[id]=cov[x],add[id]=add[x];
tag[id]=tag[x];
return id;
}
inline void flip(int x){
swap(ls[x],rs[x]),tag[x]^=1;
}
inline void cover(int x,int v){
cov[x]=v,d[x]=(LL)v*sz[x]%md,val[x]=v,add[x]=0;
}
inline void inc(int x,int v){
upd(add[x]+=v-md),d[x]=(d[x]+(LL)v*sz[x])%md,upd(val[x]+=v-md);
}
inline void update(int x){
sz[x]=sz[ls[x]]+sz[rs[x]]+1,upd(d[x]=d[ls[x]]+d[rs[x]]-md),upd(d[x]+=val[x]-md);
}
inline void pushdown(int x){
if(ls[x])ls[x]=clone(ls[x]);
if(rs[x])rs[x]=clone(rs[x]);
if(tag[x]){
tag[x]=0;
if(ls[x])flip(ls[x]);
if(rs[x])flip(rs[x]);
}
if(cov[x]!=-1){
if(ls[x])cover(ls[x],cov[x]);
if(rs[x])cover(rs[x],cov[x]);
cov[x]=-1;
}
if(add[x]){
if(ls[x])inc(ls[x],add[x]);
if(rs[x])inc(rs[x],add[x]);
add[x]=0;
}
}
inline int merge(int x,int y){
if(!x||!y)return x|y;
pushdown(x),pushdown(y);
int u;
if(mt()%(sz[x]+sz[y])<sz[x]){
u=clone(x);
rs[u]=merge(rs[x],y);
}else{
u=clone(y);
ls[u]=merge(x,ls[y]);
}
update(u);
return u;
}
inline void split(int u,int k,int&x,int&y){
if(k==0)
x=0,y=clone(u);
else
if(k==sz[u])x=clone(u),y=0;
else{
pushdown(u);
if(k<=sz[ls[u]]){
y=clone(u);
split(ls[y],k,x,ls[y]);
update(y);
}else{
x=clone(u);
split(rs[x],k-sz[ls[u]]-1,rs[x],y);
update(x);
}
}
}
void init(int&u,vector<int>&vc,int l,int r){
if(l>r){u=0;return;}
if(l==r){
u=newnode(vc[l]);
return;
}
const int mid=l+r>>1;
u=newnode(vc[mid]);
init(ls[u],vc,l,mid-1),init(rs[u],vc,mid+1,r);
update(u);
}
void build(vector<int>&vc){
n=vc.size();
rt=id=0;
init(rt,vc,0,n-1);
}
inline int query(int l,int r){
int x,y,z;
split(rt,r,x,z);
split(x,l-1,x,y);
return d[y];
}
inline void assign(int l,int r,int k){
int x,y,z;
split(rt,r,x,z);
split(x,l-1,x,y);
cover(y,k);
rt=merge(merge(x,y),z);
}
inline void modify(int l,int r,int k){
int x,y,z;
split(rt,r,x,z);
split(x,l-1,x,y);
inc(y,k);
rt=merge(merge(x,y),z);
}
inline void copyto(int l1,int r1,int l2,int r2){
int tag=0;
if(l1>l2)swap(l1,l2),swap(r1,r2),tag=1;
int _1,_2,_3,_4,_5;
split(rt,r2,rt,_5);
split(rt,l2-1,rt,_4);
split(rt,r1,rt,_3);
split(rt,l1-1,_1,_2);
if(tag)
_2=clone(_4);
else
_4=clone(_2);
rt=merge(merge(merge(merge(_1,_2),_3),_4),_5);
}
inline void swap_(int l1,int r1,int l2,int r2){
if(l1>l2)swap(l1,l2),swap(r1,r2);
int _1,_2,_3,_4,_5;
split(rt,r2,rt,_5);
split(rt,l2-1,rt,_4);
split(rt,r1,rt,_3);
split(rt,l1-1,_1,_2);
rt=merge(merge(merge(merge(_1,_4),_3),_2),_5);
}
inline void rotate(int l,int r){
int x,y,z;
split(rt,r,x,z);
split(x,l-1,x,y);
flip(y);
rt=merge(merge(x,y),z);
}
void dfs(int u,vector<int>&vc){
if(!u)return;
pushdown(u);
dfs(ls[u],vc);
vc.push_back(val[u]);
dfs(rs[u],vc);
}
inline void get_list(vector<int>&vc){
vc.clear();
dfs(rt,vc);
}
inline bool dangerous(){
return id>7.5e6;
}
inline void rebuild(){
static vector<int>vc;
vc.clear();
get_list(vc);
build(vc);
}
}
int n,q;
int main(){
n=readint(),q=readint();
vector<int>vc;
for(int i=1;i<=n;++i)
vc.push_back(readint());
T::build(vc);
int lst=0;
while(q--){
int op=readint();
switch(op){
case 1:{
int l=readint(),r=readint();
l^=lst,r^=lst;
printf("%d\n",lst=T::query(l,r));
break;
}
case 2:{
int l=readint(),r=readint(),k=readint();
l^=lst,r^=lst,k^=lst;
T::assign(l,r,k);
break;
}
case 3:{
int l=readint(),r=readint(),k=readint();
l^=lst,r^=lst,k^=lst;
T::modify(l,r,k);
break;
}
case 4:{
int l1=readint(),r1=readint(),l2=readint(),r2=readint();
l1^=lst,r1^=lst,l2^=lst,r2^=lst;
T::copyto(l1,r1,l2,r2);
break;
}
case 5:{
int l1=readint(),r1=readint(),l2=readint(),r2=readint();
l1^=lst,r1^=lst,l2^=lst,r2^=lst;
T::swap_(l1,r1,l2,r2);
break;
}
case 6:{
int l=readint(),r=readint();
l^=lst,r^=lst;
T::rotate(l,r);
}
}
if(T::dangerous())
T::rebuild();
}
T::get_list(vc);
for(int i:vc)
printf("%d ",i);
putchar('\n');
return 0;
}