fhq-treap小结
终于AC fhq-Treap啦!!!撒花庆祝qwq
回头看看fhq-treap真的吼哇!
推一波大佬的博客:https://www.cnblogs.com/ppprseter/p/9382132.html
就是看这个学会的qwq
进入正题:
fhq-treap,也具有Treap的性质,即堆+bst的性质,在此基础上,它不需要进行旋转以维护平衡,而是用拆树和合并的操作让树保持平衡
支持区间、效率高、可持久化!
规定如下:
val:按平衡树的权值
key:按堆的权值
sz:子树大小
root:当前根
ch[now][0] : 左儿子,ch[now][1]右儿子
#define ls ch[now][0] rs ch[now][1]
操作1:split(拆树)
将一个树拆成两个:
void split(int now,int k,int &x,int &y)//x <= k,y > k { if(!now) { x = y = 0; return; } if(val[now] <= k) x = now,split(rs,k,rs,y); else y = now,split(ls,k,x,ls); update(now); }
函数意义为:以now为根,将树拆成两部分,x部的权值全部<=k,y部>k
考虑当前节点now:如果val[now]<=k,那么now的左子树应当被分配给x(十分显然qwq),而now的右子树不确定,同时x的右子树也不确定,而y未被分配,因此进入下一层求解。>k同理
操作2:merge(合并)
将两个树合并,返回新树的根:
int merge(int x,int y)//val:x <= y { if(!x || !y) return x + y; if(key[x] < key[y]) { ch[x][1] = merge(ch[x][1],y); update(x); return x; } else { ch[y][0] = merge(x,ch[y][0]); update(y); return y; } }
规定:val[x] < val[y]
如果x和y中有一个为空,那么返回另一个即可
由于x,y是由split得来,因此二者均是bst,因此在合并时,只用考虑堆性质即可
有了这两个,其他的操作就围绕他们来:
新建点:不多说
int New(int k) { key[++tot] = rand(),val[tot] = k,sz[tot] = 1; return tot; }
插入:
先将树按插入数大小k分成两份,把k加到x中,再把树合并回去:
void insert(int k) { int x,y; split(root,k,x,y); root = merge(merge(x,New(k)),y); }
删除:
先把树按要删的数k分成x,y,此时x中所有数<=k,再把x按k-1分成x和z,此时z中所有数都等于k,删去z的根,就把z的左右儿子合并(相当于抛弃了z的根节点),然后再合并回去即可
void del(int k) { int x,y,z; split(root,k,x,y); split(x,k - 1,x,z); z = merge(ch[z][0],ch[z][1]); root = merge(x,merge(z,y)); }
查k的排名:
把树按k - 1分成两份(注意!不能按k分,由于可能有重,按k分无法得到正解),此时x部<=k - 1,y部>=k,输出x部大小即可
void Rank(int k) { int x,y; split(root,k - 1,x,y); printf("%d\n",sz[x] + 1); root = merge(x,y); }
找第k大:
与Treap相同
void fRank(int now,int k) { while(1) { if(k <= sz[ls]) now = ls; else if(k > sz[ls] + 1) k -= sz[ls] + 1,now = rs; else { printf("%d\n",val[now]); return;} } }
前后继:
由拆分和合并易得
传送门:https://www.luogu.org/problemnew/show/P3369
总体代码:
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<climits> #include<vector> #include<cstdlib> #include<ctime> using namespace std; #define ls ch[now][0] #define rs ch[now][1] inline int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { (ans *= 10) += ch - '0'; ch = getchar(); } return ans * op; } const int maxn = 1e5 + 5; int sz[maxn],key[maxn],val[maxn],tot,ch[maxn][2],root;//key:堆相关;val:树相关 inline void update(int now) { sz[now] = sz[ls] + sz[rs] + 1; } void split(int now,int k,int &x,int &y)//x <= k,y > k { if(!now) { x = y = 0; return; } if(val[now] <= k) x = now,split(rs,k,rs,y); else y = now,split(ls,k,x,ls); update(now); } int merge(int x,int y)//val:x <= y { if(!x || !y) return x + y; if(key[x] < key[y]) { ch[x][1] = merge(ch[x][1],y); update(x); return x; } else { ch[y][0] = merge(x,ch[y][0]); update(y); return y; } } int New(int k) { key[++tot] = rand(),val[tot] = k,sz[tot] = 1; return tot; } void insert(int k) { int x,y; split(root,k,x,y); root = merge(merge(x,New(k)),y); } void del(int k) { int x,y,z; split(root,k,x,y); split(x,k - 1,x,z); z = merge(ch[z][0],ch[z][1]); root = merge(x,merge(z,y)); } void Rank(int k) { int x,y; split(root,k - 1,x,y); printf("%d\n",sz[x] + 1); root = merge(x,y); } void fRank(int now,int k) { while(1) { if(k <= sz[ls]) now = ls; else if(k > sz[ls] + 1) k -= sz[ls] + 1,now = rs; else { printf("%d\n",val[now]); return;} } } void pre(int k) { int x,y; split(root,k - 1,x,y); fRank(x,sz[x]); root = merge(x,y); } void suc(int k) { int x,y; split(root,k,x,y); fRank(y,1); root = merge(x,y); } int main() { int n = read(); int opt,x; while(n--) { opt = read(),x = read(); if(opt == 1) insert(x); if(opt == 2) del(x); if(opt == 3) Rank(x); if(opt == 4) fRank(root,x); if(opt == 5) pre(x); if(opt == 6) suc(x); } return 0; }