正题
报表统计一点都不好玩
最烦的就是有一个Min_Gap和一个Min_Sort_Gap。
我们想怎么维护。
用一个left来表示这个子树最左边的值,用right来表示这段去见最右边的值。
那么与一个节点,我们就可以快速地知道他的前驱和后继是谁(左儿子的right和右儿子的left)。
继续向上维护left和right即可。
第二个操作相当于排完序的,所以建两棵伸展树维护排名或权值即可得知。
// luogu-judger-enable-o2
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
int n,m;
int a[500010];
struct node{
int num,son[2],f,t,more,mmin,left,right;
}s1[5000010],s2[5000010];
int image[500010];
int tot=0;
int root1=0,root2=0;
bool tf=false;
void update(int x,node* s){
if(x==0) return ;
s[x].t=s[s[x].son[0]].t+s[s[x].son[1]].t+1;
s[x].left=s[x].right=s[x].num;
s[x].mmin=1e9;
if(s[x].son[0]!=0) s[x].left=s[s[x].son[0]].left;
if(s[x].son[1]!=0) s[x].right=s[s[x].son[1]].right;
s[x].mmin=min(s[s[x].son[0]].mmin,s[s[x].son[1]].mmin);
s[x].mmin=min(s[x].mmin,min(abs(s[x].num-s[s[x].son[0]].right),abs(s[x].num-s[s[x].son[1]].left)));
}
void rotate(int x,int w,node* s){
int f=s[x].f,ff=s[f].f;
s[f].son[1-w]=s[x].son[w];
if(s[x].son[w]!=0) s[s[x].son[w]].f=f;
if(s[ff].son[1]==f) s[ff].son[1]=x;
else s[ff].son[0]=x;
s[x].f=ff;
s[x].son[w]=f;
s[f].f=x;
update(f,s);
update(x,s);
}
void splay(int x,int tar,node* s){
while(s[x].f!=tar){
int f=s[x].f,ff=s[f].f;
if(ff==tar) {
if(s[f].son[0]==x) rotate(x,1,s);
else rotate(x,0,s);
}
else{
if(s[ff].son[0]==f){
if(s[f].son[0]==x) {rotate(f,1,s);rotate(x,1,s);}
else {rotate(x,0,s);rotate(x,1,s);}
}
else{
if(s[f].son[0]==x) {rotate(x,1,s);rotate(x,0,s);}
else {rotate(f,0,s);rotate(x,0,s);}
}
}
}
}
void build(int x,int y,node* s){
tot++;
int i=tot;
int mid=(x+y)/2;
s[i].son[0]=s[i].son[1]=0;
s[i].num=a[mid];
if(!tf)image[mid]=i;
if(x<mid){
s[i].son[0]=tot+1;
s[tot+1].f=i;
build(x,mid-1,s);
}
if(mid<y){
s[i].son[1]=tot+1;
s[tot+1].f=i;
build(mid+1,y,s);
}
update(i,s);
}
void insert1(int x,int t){
int op=s1[x].more;
s1[x].more++;
splay(x,0,s1);
root1=x;
int now=s1[x].son[1];
if(op==0) now=x;
while(op!=0){
if(op<=s1[s1[now].son[0]].t) now=s1[now].son[0];
else if(op>s1[s1[now].son[0]].t+1) op=op-s1[s1[now].son[0]].t-1,now=s1[now].son[1];
else break;
}
splay(now,0,s1);
root1=now;
int pos=s1[now].son[1];
while(s1[pos].son[0]!=0 && pos!=0) pos=s1[pos].son[0];
if(pos!=0)splay(pos,now,s1);
tot++;
s1[tot].num=t;
s1[now].son[1]=tot;s1[tot].f=now;
s1[pos].f=tot;s1[tot].son[1]=pos;
update(pos,s1);
update(tot,s1);
update(now,s1);
}
void insert2(int t){
int now=root2;
while(s2[now].num!=t){
if(t<s2[now].num){
if(s2[now].son[0]==0) break;
else now=s2[now].son[0];
}
else{
if(s2[now].son[1]==0) break;
else now=s2[now].son[1];
}
}
splay(now,0,s2);
root2=now;
if(s2[now].num<t){
int pos=s2[now].son[1];
while(s2[pos].son[0]!=0 && pos!=0)pos=s2[pos].son[0];
if(pos!=0) splay(pos,now,s2);
tot++;
s2[tot].num=t;
s2[now].son[1]=tot;s2[tot].f=now;
s2[pos].f=tot;s2[tot].son[1]=pos;
update(pos,s2);
update(tot,s2);
update(now,s2);
}
else{
int pos=s2[now].son[0];
while(s2[pos].son[1]!=0) pos=s2[pos].son[1];
if(pos!=0) splay(pos,now,s2);
tot++;
s2[tot].num=t;
s2[now].son[0]=tot;s2[tot].f=now;
s2[pos].f=tot;s2[tot].son[0]=pos;
update(pos,s2);
update(tot,s2);
update(now,s2);
}
}
int main(){
s1[0].mmin=s2[0].mmin=1e9;
s1[0].num=s2[0].num=-1e9;
s1[0].left=s2[0].left=s1[0].right=s2[0].right=-1e9;
s2[0].son[0]=s2[0].son[1]=0;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
root1=root2=1;
build(1,n,s1);
sort(a+1,a+1+n);
tf=true;tot=0;
build(1,n,s2);
char c[20];
int x,y;
for(int i=1;i<=m;i++){
scanf("%s",c);
if(c[0]=='I') {
scanf("%d %d",&x,&y);
insert1(image[x],y);
tot--;
insert2(y);
}
else if(c[4]=='G') printf("%d\n",s1[root1].mmin);
else if(c[4]=='S') printf("%d\n",s2[root2].mmin);
}
}
本文详细介绍了使用伸展树解决特定问题的方法,包括如何维护子树的最小间隙和排序后的最小间隙,通过左右子节点记录边界值以快速查找前驱和后继节点,并通过两棵树分别维护排名和权值。
768

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



