题目:POJ3580.
题目大意:给定一个长度为
n
n
n的序列,现在有
m
m
m次操作支持:
1.格式
A
D
D
 
x
 
y
 
v
ADD\,x\,y\, v
ADDxyv,表示给区间
[
x
,
y
]
[x,y]
[x,y]加上
v
v
v.
2.格式
R
E
V
E
R
S
E
 
x
 
y
REVERSE\,x\,y
REVERSExy,表示翻转区间
[
x
,
y
]
[x,y]
[x,y].
3.格式
R
E
V
O
L
V
E
 
x
 
y
 
k
REVOLVE\,x\,y\,k
REVOLVExyk,表示把区间
[
x
,
y
]
[x,y]
[x,y]旋转
k
k
k次.
4.格式
I
N
S
E
R
T
 
x
 
y
INSERT\,x\,y
INSERTxy,表示在第
x
x
x个位置后面加上一个数字
y
y
y.
5.格式
D
E
L
E
T
E
 
x
DELETE\,x
DELETEx,表示删除第
x
x
x个数字.
6.格式
M
I
N
 
x
 
y
MIN\,x\,y
MINxy,表示求区间
[
x
,
y
]
[x,y]
[x,y]的最小值.
1
≤
n
,
m
≤
1
0
5
1\leq n,m\leq 10^5
1≤n,m≤105.
一道数据结构裸题,很明显用平衡树来维护.
考虑将位置作为平衡树维护的键值,但是这个键值在插入删除时很难维护所以不存,每次查找一个位置通过平衡树上二分即可.
由于区间操作众多,所以Splay无疑是处理这道题最好的平衡树.
对于区间加,我们可以直接找到位置 x − 1 x-1 x−1和 y + 1 y+1 y+1所在的节点并把 x − 1 x-1 x−1 Splay到根 y + 1 y+1 y+1 Splay成 x − 1 x-1 x−1的儿子,那么 y + 1 y+1 y+1的右儿子就是整个区间,我们称之为提取区间,打上懒标记即可.
对于区间翻转,我们先提取出区间,很明显把提取出的区间中所有节点的左右儿子交换就可以做到翻转,维护懒标记即可.
对于区间旋转,容易发现它就是把区间 [ ( y − k + 1 )    m o d    ( y − x + 1 ) , y ] [(y-k+1)\,\,mod\,\,(y-x+1),y] [(y−k+1)mod(y−x+1),y]放到 x x x的前面,所以大力提取出区间并把表示区间的子树的根的父亲先设为空.之后大力提取区间 [ y , y − 1 ] [y,y-1] [y,y−1],把子树根的父亲设为 y y y即可.
对于插入,提取区间 [ x , x − 1 ] [x,x-1] [x,x−1]并在 x x x下面新建节点即可.
对于删除,提取区间 [ x , x ] [x,x] [x,x],把 x + 1 x+1 x+1的右儿子设为空即可.
对于区间最小值,直接提取区间输出最小即可.
时间复杂度 O ( n + m log n ) O(n+m\log n) O(n+mlogn).
代码如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=100000,INF=(1<<31)-1;
//INF一定要开到2^31-1,不然会被卡
int n,a[N+9];
struct tree{
int x,fa,s[2],siz,min,add,rev;
tree(){x=fa=s[0]=s[1]=siz=add=rev=0;min=INF;}
//构造函数,初始min=INF,为了避免Pushup时要判断儿子是否为空的情况
}tr[N*2+9];
int cn,rot,tmp[N*2+9],tt;
void Pushup(int k){
int ls=tr[k].s[0],rs=tr[k].s[1];
tr[k].siz=tr[ls].siz+tr[rs].siz+1;
tr[k].min=min(tr[k].x,min(tr[ls].min,tr[rs].min));
}
void Update_rev(int k){if (!k) return;swap(tr[k].s[0],tr[k].s[1]);tr[k].rev^=1;}
void Update_add(int k,int v){if (!k) return;tr[k].add+=v;tr[k].x+=v;tr[k].min+=v;}
//建议要判掉k=0的情况不然可能会溢出
void Pushdown_rev(int k){
if (!tr[k].rev) return;
Update_rev(tr[k].s[0]);Update_rev(tr[k].s[1]);
tr[k].rev=0;
}
void Pushdown_add(int k){
if (!tr[k].add) return;
Update_add(tr[k].s[0],tr[k].add);Update_add(tr[k].s[1],tr[k].add);
tr[k].add=0;
}
void Pushdown(int k){Pushdown_rev(k);Pushdown_add(k);}
void Rotate(int x){
int y=tr[x].fa,z=tr[y].fa,k=tr[y].s[1]==x;
if (z) tr[z].s[tr[z].s[1]==y]=x;tr[x].fa=z;
tr[y].s[k]=tr[x].s[k^1];if (tr[x].s[k^1]) tr[tr[x].s[k^1]].fa=y;
tr[x].s[k^1]=y;tr[y].fa=x;
Pushup(y);Pushup(x);
}
void Splay(int x,int go){
tmp[tt=1]=x;
for (int i=x;tr[i].fa;i=tr[i].fa) tmp[++tt]=tr[i].fa;
for (;tt;--tt) Pushdown(tmp[tt]);
for (int y,z;tr[x].fa^go;Rotate(x)){
y=tr[x].fa;z=tr[y].fa;
if (z^go) tr[z].s[0]==y^tr[y].s[0]==x?Rotate(x):Rotate(y);
//一定要在这里写z^go而不是直接写z
}
if (!go) rot=x;
Pushup(x); //这道题虽然没卡,但这个Pushup有时候不写会出事
}
int New_node(int x,int fa){
tr[++cn]=tree();
tr[cn].x=tr[cn].min=x;tr[cn].siz=1;
tr[cn].fa=fa;
return cn;
}
void Build(int l,int r,int fa=0,int &k=rot){
int mid=l+r>>1;
k=New_node(a[mid],fa);
if (l<mid) Build(l,mid-1,k,tr[k].s[0]);
if (r>mid) Build(mid+1,r,k,tr[k].s[1]);
Pushup(k);
}
int Find_rank(int p,int rot=rot){
++p;
int k=rot;
while (2333){
Pushdown_rev(k);
//翻转标记会改变树的形态,在查询第k个位置时一定要下传翻转标记
if (p>tr[tr[k].s[0]].siz+1) p-=tr[tr[k].s[0]].siz+1,k=tr[k].s[1];
else if (p<=tr[tr[k].s[0]].siz) k=tr[k].s[0];
else break;
}
Splay(k,0);
return k;
}
int Insert(int p,int x){
int l=Find_rank(p),r=Find_rank(p+1);
Splay(l,0);Splay(r,l);
tr[r].s[0]=New_node(x,r);
Splay(tr[r].s[0],0);
return 0;
}
int Extract(int L,int R){
int l=Find_rank(L-1),r=Find_rank(R+1);
Splay(l,0);Splay(r,l);
return tr[r].s[0];
}
void Erase(int p){int k=tr[Extract(p,p)].fa;tr[k].s[0]=0;Splay(k,0);}
void Change_add(int L,int R,int v){int k=Extract(L,R);Update_add(k,v);Splay(k,0);}
void Change_rev(int L,int R){int k=Extract(L,R);Update_rev(k);Splay(k,0);}
int Query_min(int L,int R){int k=Extract(L,R),res=tr[k].min;Splay(k,0);return res;}
void Change_spin(int L,int R,int x){
x%=(R-L+1);
if (!x) return;
int l=Find_rank(L-1),r=Find_rank(L),t=Extract(R-x+1,R);
tr[tr[t].fa].s[0]=0;Splay(tr[t].fa,0);
Splay(l,0);Splay(r,l);
tr[r].s[0]=t;tr[t].fa=r;
Splay(t,0);
}
Abigail into(){
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
}
Abigail work(){
Build(0,n+1);
}
Abigail getans(){
char c[10];
int q,x,y,z;
scanf("%d",&q);
while (q--){
scanf("%s",c);
switch (c[0]){
case 'A':
scanf("%d%d%d",&x,&y,&z);
Change_add(x,y,z);
break;
case 'R':
if (c[3]=='E'){
scanf("%d%d",&x,&y);
Change_rev(x,y);
}else{
scanf("%d%d%d",&x,&y,&z);
Change_spin(x,y,z);
}
break;
case 'I':
scanf("%d%d",&x,&y);
Insert(x,y);
break;
case 'D':
scanf("%d",&x);
Erase(x);
break;
case 'M':
scanf("%d%d",&x,&y);
printf("%d\n",Query_min(x,y));
break;
}
}
}
int main(){
into();
work();
getans();
return 0;
}