BZOJ3224 Tyvj 1728 普通平衡树
2016-07-21 16:02
459 查看
Description
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)
Input
第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)Output
对于操作3,4,5,6每行输出一个数,表示对应答案Sample Input
101 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
Sample Output
10646584185
492737
HINT
1.n的数据范围:n<=1000002.每个数的数据范围:[-1e7,1e7]
数据如下http://pan.baidu.com/s/1jHMJwO2
正解:splay or treap or 替罪羊树
解题报告:
正解好多。
感觉写的比较好的题解:(传送门)
替罪羊树:https://zhuanlan.zhihu.com/p/21263304
treap:http://hzwer.com/1712.html
我也用几种算法都写了一遍。
替罪羊树:
感觉就是优化暴力,唯一比二叉搜索树更优秀的就是删除操作和重构操作,还是很神的。
代码如下:(我的常数取得是0.75,事实上可以再大一点)
//It is made by jump~ #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> #include <ctime> #include <vector> #include <queue> #include <map> #include <set> #ifdef WIN32 #define OT "%I64d" #else #define OT "%lld" #endif using namespace std; typedef long long LL; const int MAXN = 200011; const double A = 0.75; int n,root,tot; int ret;//判断是否需要重构 int son[MAXN][2],w[MAXN],del[MAXN],size[MAXN],zong[MAXN]; //size记录当前未被删除的结点个数,zong记录结点个数 int q[MAXN],tail,father[MAXN]; inline int getint() { int w=0,q=0; char c=getchar(); while((c<'0' || c>'9') && c!='-') c=getchar(); if (c=='-') q=1, c=getchar(); while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); return q ? -w : w; } inline bool bad(int x){ return (zong[son[x][0]]>zong[x]*A) || (zong[son[x][1]]>zong[x]*A); } inline void clear(int x,int zhi){ son[x][0]=son[x][1]=0; del[x]=0; size[x]=zong[x]=1; w[x]=zhi; } inline void insert(int x,int o,int f){ if(!o) { clear(n,x); father =f; son[f][x>=w[f]]=n; return ; } if(x>=w[o]) insert(x,son[o][1],o); else insert(x,son[o][0],o); size[o]++; zong[o]++; if(bad(o)) ret=o; } inline void update(int x){ int l=son[x][0],r=son[x][1]; size[x]=size[l]+size[r]+(del[x]^1); zong[x]=zong[l]+zong[r]+1; } inline void build(int l,int r,int f,int fx){ if(l>r) return ; int mid=(l+r)/2; son[q[mid]][0]=son[q[mid]][1]=father[q[mid]]=del[q[mid]]=0; if(!f) root=q[mid]; else father[q[mid]]=f; son[f][fx]=q[mid]; if(l==r) {size[q[l]]=1;zong[q[l]]=1;son[q[l]][0]=son[q[l]][1]=0;return;}//!!! build(l,mid-1,q[mid],0); build(mid+1,r,q[mid],1); update(q[mid]); } inline void mid_dfs(int x){//中序遍历 if(son[x][0]) mid_dfs(son[x][0]); if(!del[x]) q[++tail]=x; if(son[x][1]) mid_dfs(son[x][1]); } inline void rebuild(int x){//替罪羊树的重构 tail=0; mid_dfs(x); build(1,tail,father[x],(son[father[x]][1]==x)); } inline int rank(int x){//求x的排名 int t=root; int now=1; while(t) { if(x<=w[t]) t=son[t][0]; else { now+=(size[son[t][0]]+(del[t]^1)); t=son[t][1]; } } return now; } inline int kth(int x){//查找排名为x的数 int t=root; while(t) { if(x==size[son[t][0]]+1 && !del[t]) return w[t]; if(size[son[t][0]]>=x) t=son[t][0]; else { x-=size[son[t][0]]+(del[t]^1); t=son[t][1]; } } } inline void out(int x,int o){//删除排名为x的一个点,删除的点打上标记即可 size[o]--; int p=size[son[o][0]]+(del[o]^1); if(p==x && !del[o]) { del[o]=1; return ; } if(x<=p) out(x,son[o][0]); else out(x-p,son[o][1]); } inline void work(){ int T=getint(); int ljh,x; while(T--) { ljh=getint(); x=getint(); if(ljh==1) { n++; tot++; if(tot==1) { root=1; clear(1,x); } else { ret=0; insert(x,root,0); if(ret) rebuild(ret); } } else if(ljh==2) { tot--; out(rank(x+1)-1,root); if(size[root]<zong[root]*A) rebuild(root); }//已经删除的结点个数超过设定限制 else if(ljh==3) printf("%d\n",rank(x)); else if(ljh==4) printf("%d\n",kth(x)); else if(ljh==5) printf("%d\n",kth(rank(x)-1)); else printf("%d\n",kth(rank(x+1))); } } int main() { work(); return 0; }