前言:
由于博主太懒,只是整理了一下,主要还是借鉴别人的博文
推荐一篇:史上最详尽的平衡树(splay)讲解与模板
普通平衡树:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define MAXN 1000000
int ch[MAXN][2],f[MAXN],size[MAXN],cnt[MAXN],key[MAXN];
int sz,root;
void R(int &x)
{
int f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
void clear(int x){
ch[x][0]=ch[x][1]=f[x]=key[x]=cnt[x]=size[x]=0;
}
int get(int x){
return x==ch[f[x]][1];
}
void update(int x){
if (x) size[x]=cnt[x]+size[ch[x][0]]+size[ch[x][1]];
}
void rotate(int x){
int old=f[x],oldf=f[old],which=get(x);
f[old]=x;ch[old][which]=ch[x][which^1];
f[ch[old][which]]=old;ch[x][which^1]=old;
f[x]=oldf;
if (oldf)
ch[oldf][ch[oldf][1]==old]=x;
update(old);update(x);
}
void splay(int x){
int fa;
while(f[x]!=0){
fa=f[x];
if (f[fa]!=0)
{
if (get(fa)==get(x)) rotate(fa);
else rotate(x);
}
rotate(x);
}
root=x;
}
void insert(int x){
if (!sz){
sz++;
root=1;
ch[1][0]=ch[1][1]=f[1]=0;
size[1]=cnt[1]=1;
key[1]=x;
return;
}
int now=root,fa=0;
while (1){
if (key[now]==x){
cnt[now]++;
update(now);
update(fa);
splay(now);
break;
}
fa=now;
now=ch[fa][key[now]<x];
if (!now){
sz++;
ch[sz][0]=ch[sz][1]=0;
f[sz]=fa;
key[sz]=x;
size[sz]=cnt[sz]=1;
ch[fa][key[fa]<x]=sz;
update(fa);
splay(sz);
break;
}
}
}
int find(int x){
int now=root,ans=0;
while (1){
if (x<key[now]) now=ch[now][0];
else{
ans+=size[ch[now][0]];
if (key[now]==x)
{
splay(now);
return ans+1;
}
ans+=cnt[now];
now=ch[now][1];
}
}
}
int findx(int x){
int now=root;
while (1){
if (x<=size[ch[now][0]])
now=ch[now][0];
else{
int t=size[ch[now][0]]+cnt[now];
if (x<=t) return key[now];
x-=t;
now=ch[now][1];
}
}
}
int pre(){
int now=ch[root][0];
while (ch[now][1]) now=ch[now][1];
return now;
}
int next(){
int now=ch[root][1];
while (ch[now][0]) now=ch[now][0];
return now;
}
void del(int x){
int d=find(x);
if (cnt[root]>1){
cnt[root]--;
update(root);
return;
}
if (!ch[root][0]&&!ch[root][1]) { sz=root=0; clear(root); return; }
if (!ch[root][0]){
int old=root;
root=ch[root][1];
f[root]=0;
clear(old);
return;
}
if (!ch[root][1]){
int old=root;
root=ch[root][0];
f[root]=0;
clear(old);
return;
}
int l=pre(),old=root;
splay(l);
ch[root][1]=ch[old][1];
f[ch[old][1]]=root;
clear(old);
update(root);
}
int main(){
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
int n,opt,x;
scanf("%d",&n);
for (int i=1;i<=n;++i){
R(opt),R(x);
switch(opt){
case 1: insert(x); break;
case 2: del(x); break;
case 3: printf("%d\n",find(x)); break;
case 4: printf("%d\n",findx(x)); break;
case 5: insert(x); printf("%d\n",key[pre()]); del(x); break;
case 6: insert(x); printf("%d\n",key[next()]); del(x); break;
}
}
return 0;
}
众所周知splay是序列之王,那么我们怎么用它来维护序列呢?首先我们认为splay的中序遍历即为原序列,假设我们要区间l,r操作,那么我们将l-1提到root,r+1提到root的右儿子,那么r+1的左儿子对应的区间就是我们要操作的区间,一般为了防止询问1,n我们会设置两个虚拟节点。
记得在rotate和find的时候都要标记下传。
文艺平衡树:
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)
using namespace std;
const int N=100000+55;
const int inf=2147483647;
int ch[N][2],f[N],key[N],size[N],a[N],root,sz,tag[N];
void R(int &x)
{
int f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
int get(int x){
return x==ch[f[x]][1];
}
void update(int x){
if (x) size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
}
void pushdown(int x){
if (x&&tag[x]){
tag[ch[x][0]]^=1;
tag[ch[x][1]]^=1;
swap(ch[x][0],ch[x][1]);//下传标记别忘了交换左右儿子
tag[x]=0;
}
}
void rotate(int x){
pushdown(f[x]);//记得顺序
pushdown(x);
int old=f[x],oldf=f[old],which=get(x);
f[old]=x;ch[old][which]=ch[x][which^1];
f[ch[old][which]]=old;ch[x][which^1]=old;
f[x]=oldf;
if(oldf)
ch[oldf][ch[oldf][1]==old]=x;
update(old);update(x);//注意update的顺序
}
void splay(int x,int y){
/*for (int fa;(fa=f[x])^y;rotate(x))
if (f[fa]^y){
rotate(get(fa)==get(x)?fa:x);
}*/
int fa;
while (f[x]^y){
fa=f[x];
if (f[fa]^y){
rotate(get(fa)==get(x)?fa:x);
}
rotate(x);
}
if (!y) root=x;
}
int find(int x){
int now=root;
while (1){
pushdown(now);
if (x<=size[ch[now][0]]) now=ch[now][0];
else{
x-=size[ch[now][0]]+1;
if (!x) return now;
now=ch[now][1];
}
}
}
int build(int l,int r,int fa){
if (l>r) return 0;
int m=l+r>>1,now=++sz;
key[now]=a[m];
f[now]=fa;
if(l^r){
ch[now][0]=build(l,m-1,now);
ch[now][1]=build(m+1,r,now);
}
update(now);
return now;
}
void print(int now){
pushdown(now);
if (ch[now][0]) print(ch[now][0]);
if (key[now]!=inf&&key[now]!=-inf)printf("%d ",key[now]);
if (ch[now][1]) print(ch[now][1]);
}
int main(){
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
int n,m,l,r,x,y;
R(n);R(m);
a[1]=-inf;
a[n+2]=inf;
fo(i,1,n)
a[i+1]=i;
root=build(1,n+2,0);
while (m--){
R(l);R(r);
x=find(l);
y=find(r+2);
splay(x,0);
splay(y,x);
tag[ch[ch[root][1]][0]]^=1;
}
print(root);
return 0;
}
jzoj4216
给出一个N个整数构成的序列,有M次操作,每次操作有一下三种:
①Insert Y X,在序列的第Y个数之前插入一个数X;
②Add L R X,对序列中第L个数到第R个数,每个数都加上X;
③Query L R,询问序列中第L个数到第R个数的平方和。
这题可以用线段树离线做,但是splay更简单。
插入的话就是,把x-1提到root,把x提到x-1的儿子,插入的这个点就是x的左儿子,平方和就维护一下区间和,区间大小,另外,在这题中,我没有建树,而是先把它,弄成一条链,接着再通过splay把它进行调整
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
const int N=200010;
const int mo=7459;
int key[N],ch[N][2],size[N],f[N],t[N],s[N],tag[N],root,sz,d[N];
void R(int &x)
{
int f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
int get(int x){
return x==ch[f[x]][1];
}
void update(int x){
size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
s[x]=(s[ch[x][0]]+s[ch[x][1]]+key[x])%mo;
t[x]=(t[ch[x][0]]+t[ch[x][1]]+key[x]*key[x]%mo)%mo;
}
void add(int x,int k){
if (x){
t[x]=(t[x]+2*s[x]%mo*k%mo+size[x]*k%mo*k%mo)%mo;
s[x]=(s[x]+size[x]*k)%mo;
key[x]=(key[x]+k)%mo;
tag[x]=(tag[x]+k)%mo;
}
}
void pushdown(int x){
if (tag[x]){
add(ch[x][0],tag[x]);
add(ch[x][1],tag[x]);
tag[x]=0;
}
}
void rotate(int x){
pushdown(f[x]);
pushdown(x);
int old=f[x],oldf=f[old],which=get(x);
f[old]=x;ch[old][which]=ch[x][which^1];
f[ch[old][which]]=old;ch[x][which^1]=old;
f[x]=oldf;
if(oldf)
ch[oldf][ch[oldf][1]==old]=x;
update(old);update(x);
}
void splay(int x,int y){
for (int fa;(fa=f[x])^y;rotate(x))
if (f[fa]^y){
rotate(get(fa)==get(x)?fa:x);
}
if (!y) root=x;
}
int find(int x){
int now=root;
while (1){
pushdown(now);
if (x<=size[ch[now][0]]) now=ch[now][0];
else{
x-=size[ch[now][0]]+1;
if (!x) return now;
now=ch[now][1];
}
}
}
int F(int x){
return((x%mo)+mo)%mo;
}
int main(){
//freopen("jzoj4216.in", "r", stdin);
//freopen("jzoj4216.out", "w", stdout);
int n,m,x,y,l,r,v;
char s1[10];
R(n);
size[1]=1;
fo(i,1,n){
R(key[i+1]);
f[i]=i+1;ch[i+1][0]=i;update(i+1);
}
f[n+1]=sz=root=n+2;ch[n+2][0]=n+1;update(n+2);
R(m);
while (m--){
scanf("%s",s1);R(x);R(y);
if (s1[0]=='I'){
l=find(x);r=find(x+1);
//printf("%d %d\n",l,r);
splay(l,0);splay(r,l);
sz++;
f[sz]=r;ch[r][0]=sz;size[sz]=1;key[sz]=y;
s[sz]=y;t[sz]=y*y%mo;
update(r);
splay(sz,0);
}
if (s1[0]=='A'){
R(v);
l=find(x);r=find(y+2);
//printf("%d %d\n",l,r);
splay(l,0);
splay(r,l);
add(ch[r][0],v);
//splay(ch[r][0],0);
}
if (s1[0]=='Q'){
l=find(x);r=find(y+2);
//printf("%d %d\n",l,r);
splay(l,0);splay(r,l);
printf("%d\n",F(t[ch[r][0]]));
}
}
}
后记:
splay虽然很强大,但是却也有它做不了的东西,比如优化连边(线段树),可持久化平衡树(treap),而且splay也有时会被卡常,但是还是挺好用的,膜拜tarjan。