BZOJ 3224 普通平衡树
AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=3224
平衡树的模板题,Splay操作可以让平均复杂度降到log(n)。
插入,即二叉搜索树的插入
删除,先Splay要删除的节点,然后将左右子树合并
前驱后继都可以直接用二叉搜索树的功能。
查询数的排名的时候,只需要Splay这个数,然后求左子树的节点个数就好了。
查询排名第k的时候,需要发挥神奇的记录存在这个节点下的子树上的节点个数的属性,然后利用搜索二叉树的方法,进行二分搜索,判断位置。
我的代码很长...但是我觉得还是蛮好看的恩...因为没有看过别人的模板...
数据结构题真的调好久啊,好久啊
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=100010; inline int in(){ int x=0,flag=1;char ch=getchar(); while((ch>'9' || ch<'0') && ch!='-') ch=getchar(); if(ch=='-') flag=-1,ch=getchar(); while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); return x*flag;/*忘记乘flag*/ } int cnt; struct Node{ int data,sum,cot; int l,r,f; }s[maxn]; inline void updata(int a){ s[a].sum=s[s[a].l].sum+s[s[a].r].sum+s[a].cot; } inline void up_updata(int a){ while(a){ s[a].sum=s[s[a].l].sum+s[s[a].r].sum+s[a].cot; a=s[a].f; } } void print(int Root){ if(!Root) return; print(s[Root].l); for(int i=1;i<=s[Root].cot;i++) printf("%d ",s[Root].data); print(s[Root].r); } void zig(int x){ int y=s[x].f; s[y].l=s[x].r; if(s[x].r) s[s[x].r].f=y; s[x].f=s[y].f; if(s[y].f){ if(y==s[s[y].f].l) s[s[y].f].l=x; else s[s[y].f].r=x; } s[y].f=x,s[x].r=y; updata(y),updata(x);/*这里顺序反过一次*/ } void zag(int x){ int y=s[x].f; s[y].r=s[x].l; if(s[x].l) s[s[x].l].f=y; s[x].f=s[y].f; if(s[y].f){ if(y==s[s[y].f].l) s[s[y].f].l=x; else s[s[y].f].r=x; } s[y].f=x,s[x].l=y; updata(y),updata(x); } void Splay(int x,int &Root){ int y; while(s[x].f){ y=s[x].f; if(!s[y].f){ if(x==s[y].l) zig(x); else zag(x); break; } if(x==s[y].l){ if(y==s[s[y].f].l) zig(y),zig(x); else zig(x),zag(x); } else{ if(y==s[s[y].f].r) zag(y),zag(x); else zag(x),zig(x); } } Root=x; } int Find(int x,int &Root){ int p=Root; while(p){ if(s[p].data==x) break; else if(s[p].data>x) p=s[p].l; else p=s[p].r; } if(!p) return 0; Splay(p,Root); return p; } void Insert(int x,int &Root){ int p=Root; if(!p){ Root=++cnt,s[cnt].f=0,s[cnt].data=x,s[cnt].cot=s[cnt].sum=1; return; } while(p){ if(s[p].data==x){ s[p].cot++;up_updata(p)/*这里打成cnt*/;break; } else if(s[p].data>x){ if(s[p].l) p=s[p].l; else{ s[p].l=++cnt,s[cnt].f=p,s[cnt].data=x,s[cnt].cot=1,up_updata(cnt); break; } } else{ if(s[p].r) p=s[p].r; else{ s[p].r=++cnt,s[cnt].f=p,s[cnt].data=x,s[cnt].cot=1,up_updata(cnt); break; } } } } int find_max(int &Root){ int p=Root; while(s[p].r) p=s[p].r; Splay(p,Root); return p; } int find_min(int &Root){ int p=Root; while(s[p].l) p=s[p].l; Splay(p,Root); return p; } int pre(int x,int &Root){ int p=Root,last=p; while(p) if(s[p].data>=x) p=s[p].l; else last=p,p=s[p].r; return last; } int las(int x,int &Root){ int p=Root,last=p; while(p) if(s[p].data<=x) p=s[p].r; else last=p,p=s[p].l; return last; } int Union(int &Root1,int Root2){ if(!Root1) {s[Root2].f=0;return Root2;} if(!Root2) {s[Root1].f=0;return Root1;} int p=find_max(Root1); s[p].r=Root2; s[Root2].f=p; updata(p); return p; } void Delete(int x,int &Root){ int p=Find(x,Root); if(s[p].cot==1) Root=Union(s[p].l,s[p].r); else s[p].cot--,s[p].sum--; } int get_rank(int x,int &Root){ int p=Find(x,Root); return s[s[p].l].sum; } int get_num(int k,int Root){ int p=Root; while(p){ if(k<=s[s[p].l].sum) p=s[p].l; else{ k-=s[s[p].l].sum; if(k<=s[p].cot) return s[p].data; else k-=s[p].cot,p=s[p].r; } } return 0; } int main(){ #ifndef ONLINE_JUDGE freopen("3224.in","r",stdin); freopen("3224.out","w",stdout); #endif int kase=in(),ord,x,Root=0; while(kase--){ ord=in();x=in(); if(ord==1) Insert(x,Root); else if(ord==2) Delete(x,Root); else if(ord==3) printf("%d\n",get_rank(x,Root)+1); else if(ord==4) printf("%d\n",get_num(x,Root)); else if(ord==5) printf("%d\n",s[pre(x,Root)].data); else printf("%d\n",s[las(x,Root)].data); //print(Root);putchar('\n'); } return 0; }
附送这题的make_data,虽然做的有点丑吧...但是应该不会造出错误的数据...
#include<cstdio> #include<cstring> #include<cstdlib> #include<ctime> #include<cmath> #include<algorithm> using namespace std; int rec[10010]; bool cmp(const int a,const int b){ //return a<b; //return cos(a)<sin(b); return rand()%10<rand()%10; } int main(){ freopen("3224.in","w",stdout); srand(time(0)); int cnt=0,t=rand()%1+100000; printf("%d\n",t); for(int i=1;i<=t;i++){ int ord=rand()%6+1,x=rand()%100000+1; if(cnt<=1) ord=1; if(ord==1){ rec[++cnt]=x; printf("1 %d\n",x); } else if(ord==2){ printf("2 %d\n",rec[cnt]);rec[cnt--]=0; } else if(ord==3){ printf("3 %d\n",rec[(rand()%cnt)+1]); } else if(ord==4) printf("4 %d\n",rand()%cnt+1); else if(ord==5){ if(cnt>2000) sort(rec+1,rec+cnt+1,cmp); printf("5 %d\n",rec[(rand()%cnt)+1]+1); } else if(ord==6){ //sort(rec+1,rec+cnt+1,cmp); printf("6 %d\n",rec[(rand()%cnt)+1]-1); } } return 0; }
对拍加油啦...