题目链接:BZOJ 1500
听说能将这道题写过splay就没有问题了吧= =。去看hzwer的博客他说遇到这种恶心的题,找一个好心的学长要一个板吧。所以我就用了他的板了。查错查了好久最后才发现是update中一个l写成了r。Orzzzzzzzzzzzz。
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define inf (1e9)
#define maxn (1000000+3)
int N,M,root,size;
int fa[maxn],c[maxn][2],id[maxn];
int a[maxn],v[maxn],lmx[maxn],rmx[maxn],sum[maxn],mx[maxn],siz[maxn],rec[maxn];
bool tag[maxn],rev[maxn];
void pushup(int k){
int l=c[k][0],r=c[k][1];
siz[k]=siz[l]+siz[r]+1;
sum[k]=sum[l]+sum[r]+v[k];
mx[k]=max(mx[l],mx[r]);
mx[k]=max(mx[k],rmx[l]+lmx[r]+v[k]);
lmx[k]=max(lmx[l],sum[l]+v[k]+lmx[r]);
rmx[k]=max(rmx[r],sum[r]+v[k]+rmx[l]);
}
void pushdown(int k){
int l=c[k][0],r=c[k][1];
if(tag[k]){
tag[k]=rev[k]=0;
if(l){tag[l]=1; v[l]=v[k]; sum[l]=v[k]*siz[l];}
if(r){tag[r]=1; v[r]=v[k]; sum[r]=v[k]*siz[r];}
if(v[k]>=0){
if(l)lmx[l]=rmx[l]=mx[l]=sum[l];
if(r)lmx[r]=rmx[r]=mx[r]=sum[r];
}
else{
if(l)lmx[l]=rmx[l]=0,mx[l]=v[k];
if(r)lmx[r]=rmx[r]=0,mx[r]=v[k];
}
}
if(rev[k]){
rev[k]^=1; rev[l]^=1; rev[r]^=1;
swap(lmx[l],rmx[l]); swap(lmx[r],rmx[r]);
swap(c[l][0],c[l][1]);swap(c[r][0],c[r][1]);
pushup(k);
}
}
void rotate(int x,int &k){
int y=fa[x],z=fa[y],l,r;
if(c[y][0]==x)l=0; else l=1; r=l^1;
if(y==k)k=x;
else{
if(c[z][0]==y)c[z][0]=x;
else c[z][1]=x;
}
fa[c[x][r]]=y; fa[y]=x; fa[x]=z;
c[y][l]=c[x][r]; c[x][r]=y;
pushup(y); pushup(x);
}
void splay(int x,int &k){
while(x!=k){
int y=fa[x],z=fa[y];
if(y!=k){
if((c[y][0]==x)==(c[z][0]==y))rotate(y,k);
else rotate(x,k);
}
rotate(x,k);
}
}
int find(int k,int rk){
if(tag[k]||rev[k])pushdown(k);
int l=c[k][0],r=c[k][1];
if(siz[l]+1==rk)return k;
else if(siz[l]>=rk)return find(l,rk);
else return find(r,rk-siz[l]-1);//注意还要减去根节点
}
void built(int l,int r,int f){
if(l>r)return ;
int now=id[l],last=id[f];
if(l==r){
v[now]=mx[now]=sum[now]=a[l];
fa[now]=last; siz[now]=1;
if(a[l]>=0)lmx[now]=rmx[now]=a[l];//更新最大和
else lmx[now]=rmx[now]=0;
if(l<f)c[last][0]=now;
else c[last][1]=now;
return ;
}
int mid=(l+r)>>1; now=id[mid];
built(l,mid-1,mid); built(mid+1,r,mid);
v[now]=a[mid]; fa[now]=last; pushup(now);
if(mid<f)c[last][0]=now;
else c[last][1]=now;
}
void ins(int k,int tot){
int x,y,z;
for(int i=1;i<=tot;i++){
scanf("%d",&a[i]);
if(rec[0])id[i]=rec[rec[0]--];
else id[i]=++size;
}
built(1,tot,0);
z=id[(tot+1)>>1];//加入的这一段的根节点
x=find(root,k+1); y=find(root,k+2);//因为我们加入了1这个虚拟节点,所以实际右移一位
splay(x,root); splay(y,c[x][1]);
c[y][0]=z; fa[z]=y;
pushup(y); pushup(x);
}
void reclaim(int k){
if(!k)return ;
int l=c[k][0],r=c[k][1];
reclaim(l); reclaim(r);
fa[k]=c[k][0]=c[k][1]=0;
tag[k]=rev[k]=0;
rec[++rec[0]]=k;
}
void del(int k,int tot){
int x,y,z;
x=find(root,k); y=find(root,k+tot+1);
splay(x,root); splay(y,c[x][1]);
z=c[y][0]; reclaim(z); c[y][0]=0;
pushup(y); pushup(x);
}
void solve_change(int k,int val){
v[k]=val; tag[k]=1;//tag相当于lazy操作
sum[k]=v[k]*siz[k];
if(val>=0)lmx[k]=rmx[k]=mx[k]=sum[k];
else lmx[k]=rmx[k]=0,mx[k]=v[k];
}
void change(int k,int tot,int val){
int x,y,z;
x=find(root,k); y=find(root,k+tot+1);
splay(x,root); splay(y,c[x][1]);
z=c[y][0];
solve_change(z,val);
pushup(y); pushup(x);
}
void solve_rever(int k){
if(tag[k])return ;//已经将该区间的数全部变为相等的数,反转就没有意义
rev[k]^=1;//rev标记是否要反转
swap(c[k][0],c[k][1]);
swap(lmx[k],rmx[k]);
}
void rever(int k,int tot){
int x,y,z;
x=find(root,k); y=find(root,k+tot+1);
splay(x,root); splay(y,c[x][1]);
z=c[y][0];
solve_rever(z);
pushup(y); pushup(x);//注意先更新y再更新x(因为此时y为x的右儿子)
}
void query(int k,int tot){
int x,y,z;
x=find(root,k); y=find(root,k+tot+1);
splay(x,root); splay(y,c[x][1]);
z=c[y][0];
printf("%d\n",sum[z]);
}
int main(){
scanf("%d%d",&N,&M);
mx[0]=-inf;
a[1]=a[N+2]=-inf;
for(int i=1;i<=N;i++)scanf("%d",&a[i+1]);
for(int i=1;i<=N+2;i++)id[i]=i;
built(1,N+2,0);//建立1和N+2这两个虚拟节点
root=(N+3)>>1; size=N+2;
char s[10];
int k,tot,val;
for(int i=1;i<=M;i++){
scanf("%s",s);
switch(s[0]){
case 'I':scanf("%d",&k);scanf("%d",&tot);ins(k,tot);break;
case 'D':scanf("%d",&k);scanf("%d",&tot);del(k,tot);break;
case 'M':
if(s[2]=='K'){
scanf("%d%d%d",&k,&tot,&val); change(k,tot,val);
}
else printf("%d\n",mx[root]);
break;
case 'R':scanf("%d%d",&k,&tot);rever(k,tot);break;
case 'G':scanf("%d%d",&k,&tot);query(k,tot);break;
}
}
return 0;
}
本文详细解析了BZOJ1500题目的Splay树实现方法,介绍了Splay树的基本操作如旋转、下推、上拉等,并通过具体实例展示了插入、删除、修改等复杂操作的具体实现过程。
497

被折叠的 条评论
为什么被折叠?



