一种简单粗暴的数据结构(我自认为暴力“不优雅”。。。)
和其他“优雅”的splay,fhq-treap不同
替罪羊既不旋转,也不分裂合并
我看谁不顺眼,直接让其暴力重构!
思路就是这样。
特点是:
1.如果一个点的左子树或者右子树的节点个数>x的节点个数*alpha(0.5~1.0之间的一个值),那么暴力重构整个子树
2.懒惰删除,因为SGT不够灵活。对删除的点打上标记,并且在祖先节点更新信息。但是不删掉节点本身(名存实亡)。
3.如果一个点子树的已经删除部分占30%左右,暴力重构子树
所以,一个点的信息是:
sz:子树节点个数
cnt:子树实际存在的节点个数
val:自己的权值
ch[2]:两个儿子
die:是否被删除
先说暴力重构:
分三步:
1.dfs(x),把x子树所有没有删除的点加入一个数组里
2.build(l,r),分治建树
3.pushup(fa[x])把x的father的信息更新一下(node中可以不记录father,临时记录即可)
操作:
(最大的坑莫过于未考虑到名存实亡的点)
insert:类比splay,while循环,找到位置之后加入节点(可以同一个值分成不同的点,x和左右子树的关系变成不严格的即可)
最后从上而下尝试rebuild
dele:找到后die=1,
注意:必须找到一个没有被删除的。所以,不用权值找,而用第k大(坑了2h)
(权值的话,如果x被删除了你不知道往哪里走)
最后从上而下尝试rebuild
rank,kth,pre,bac都差不多,kth的返回,注意当前点必须是未删除点
复杂度:alpha调好,可以保证O(nlogn)(alpha一般是0.75)
代码:
// luogu-judger-enable-o2 #include<bits/stdc++.h> #define il inline #define reg register int #define numb (ch^‘0‘) using namespace std; typedef long long ll; il void rd(int &x){ char ch;bool fl=false; while(!isdigit(ch=getchar()))(ch==‘-‘)&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } namespace Miracle{ const int N=100000+5; const double al=0.75; struct node{ int ch[2],val; int sz,cnt; bool die; void init(int v){ sz=1;cnt=1;val=v;ch[0]=0;ch[1]=0; } }t[N]; void pushup(int x){ if(!x) return; t[x].sz=t[t[x].ch[0]].sz+t[t[x].ch[1]].sz+1; t[x].cnt=t[t[x].ch[0]].cnt+t[t[x].ch[1]].cnt+(!t[x].die); } int rt; int tot=0; int q[N],num; int isbad(int x){ return (t[t[x].ch[0]].sz>t[x].sz*al||t[t[x].ch[1]].sz>t[x].sz*al||t[x].cnt<t[x].sz*al); } void dfs(int x){ if(!x) return ; dfs(t[x].ch[0]); if(!t[x].die) { q[++num]=x; // cout<<t[x].val<<" "; } dfs(t[x].ch[1]); } int build(int l,int r){//warning!! no consider fa if(l>r) return 0; int mid=(l+r)>>1; int x=q[mid]; // t[x].fa=0; t[x].ch[0]=build(l,mid-1); t[x].ch[1]=build(mid+1,r); // t[t[x].ch[0]].fa=x; // t[t[x].ch[1]].fa=x; pushup(x); return x; } void rebuild(int &x){ //cout<<" rerererreuildbuilbublilbiulib "<<x<<endl; //cout<<" xxx "<<x<<endl; num=0; //x=2;return; //int y=t[x].fa; // int d=t[y].ch[1]==x; dfs(x); //cout<<" num "<<num<<endl;; // for(reg i=1;i<=num;++i){ // cout<<t[q[i]].val<<" "; // }cout<<endl; x=build(1,num); // cout<<" xx "<<x<<endl; pushup(x); } int sta[N],top; void ins(int v){ if(!rt){ rt=++tot; t[rt].init(v); return; } int x=rt; top=0; while(1){ int d=t[x].val<=v; ++t[x].sz; ++t[x].cnt; sta[++top]=x; if(!t[x].ch[d]){ t[x].ch[d]=++tot; t[tot].init(v); break; } x=t[x].ch[d]; } int las=0; for(reg i=1;i<=top;++i){ if(isbad(sta[i])){ if(las==0) { // cout<<" rrt "<<endl, rebuild(rt); // cout<<rt<<" val "<<t[rt].val<<endl; } else rebuild(t[las].ch[t[las].ch[1]==sta[i]]),pushup(las); return; } las=sta[i]; } } void del(int k){ int x=rt; top=0; while(1){ int d=t[t[x].ch[0]].cnt+(!t[x].die); sta[++top]=x; --t[x].cnt; if(!t[x].die&&d==k) { t[x].die=1; break; } if(t[t[x].ch[0]].cnt>=k) x=t[x].ch[0]; else{ k-=d;x=t[x].ch[1]; } } int las=0; for(reg i=1;i<=top;++i){ if(isbad(sta[i])){ if(las==0) rebuild(rt); else //cout<<" las "<<las<<endl, rebuild(t[las].ch[t[las].ch[1]==sta[i]]),pushup(las); return; } las=sta[i]; } } int kth(int k){ int x=rt; while(1){ // cout<<" xx "<<x<<" "<<t[x].val<<" "<<t[x].sz<<" "<<t[x].cnt<<endl; int d=t[t[x].ch[0]].cnt+(!t[x].die); if(!t[x].die&&d==k) return t[x].val; if(t[t[x].ch[0]].cnt>=k) x=t[x].ch[0]; else{ k-=d;x=t[x].ch[1]; } } return -23333333; } int rk(int v){ int x=rt; int ret=1; while(x){ int d=t[x].val<v; if(d) ret+=t[t[x].ch[0]].cnt+(!t[x].die),x=t[x].ch[1]; else x=t[x].ch[0]; } //cout<<" ret "<<ret<<endl; return ret; } int n; int main(){ rd(n); int op,x; for(reg i=1;i<=n;++i){ rd(op);rd(x); if(op==1) ins(x); else if(op==2) del(rk(x)); else if(op==3) printf("%d\n",rk(x)); else if(op==4) printf("%d\n",kth(x)); else if(op==5) printf("%d\n",kth(rk(x)-1)); else printf("%d\n",kth(rk(x+1))); // cout<<" rt "<<rt<<" "<<t[rt].val<<" :: "<<t[rt].sz<<" "<<t[rt].cnt<<endl; } return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2019/2/5 19:28:58 */
原文:https://www.cnblogs.com/Miracevin/p/10353237.html