Treap树 = tree+heap
数和堆的集合,每个节点有值val,也有优先级key,那么这棵树的形态就被确定了,和插入顺序无关了(有赖于优先级
避免退化成链:再生成节点时,随机生成优先级,然后插入时动态调整
有旋treap:依靠旋转来维持heap的平衡
1、FHQ treap又称无旋treap,没有旋转操作,使用分裂和合并两个操作维护树的平衡
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | struct node{ int l,r; int val; int key; int size; }tr[N]; int root,idx; int newnode( int v){ } void pushup(){ //向上更新 } void split( int p, int v, int &x, int &y){ //分裂操作 注意在递归的过程中,连接了分裂后的新边 } int merg( int x, int y){ //合并,根据key的大小,注意在递归的过程中,连接了分裂后的新边 } void inser( int v){ //插入节点,先分裂,再合并,连续两次合并 } void del( int v){ //删除操作,先分裂、再合并 } int get_k( int p, int k){ //返回第k个节点 } void get_pre( int v){ //找前驱 } void get_suc( int v){ //找后继 } void get_rank( int v){ //排名 } void get_val( int k){ //数值 } |
P3369 【模板】普通平衡树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | #include <iostream> #include <cstring> #include <algorithm> using namespace std; const int maxn=1e5+10; struct node{ int l,r; int val; //树的权值 int key; //堆的随机值 int size; }tr[maxn]; int root,idx; int newnode( int &x, int v){ x=++idx; tr[x].val=v; tr[x].key= rand (); tr[x].size=1; } void pushup( int p){ //向上更新 tr[p].size=tr[tr[p].l].size+tr[tr[p].r].size+1; } void split( int p, int v, int &x, int &y){ //分裂操作 注意在递归的过程中,连接了分裂后的新边 if (!p) {x=y=0; return ; } if (tr[p].val<=v){ x=p; split(tr[x].r,v,tr[x].r,y); pushup(x); } else { y=p; split(tr[y].l,v,x,tr[y].l); pushup(y);; } } int merg( int x, int y){ //合并,根据key的大小,注意在递归的过程中,连接了分裂后的新边 if (!x||!y) return x+y; if (tr[x].key<tr[y].key){ tr[x].r=merg(tr[x].r,y); pushup(x); return x; } else { tr[y].l=merg(x,tr[y].l); pushup(y); return y; } } void inser( int v){ //插入节点,先分裂,再合并,连续两次合并 int x,y,z; split(root,v,x,y); newnode(z,v); root=merg(merg(x,z),y); } void del( int v){ //删除操作,先分裂、再合并 int x,y,z; split(root,v,x,z); split(x,v-1,x,y); y=merg(tr[y].l,tr[y].r); root=merg(merg(x,y),z); } //int get_k(int p,int k){ //返回第k个节点 // //} int getrank( int v){ //排名 int x,y; split(root,v-1,x,y); int ans=tr[x].size+1; root=merg(x,y); return ans; } int getval( int root, int v){ //数值 if (v==tr[tr[root].l].size+1) return tr[root].val; else if (v<=tr[tr[root].l].size) return getval(tr[root].l,v); else return getval(tr[root].r,v-tr[tr[root].l].size-1); } int getpre( int v){ //找前驱 int x,y,s,ans; split(root,v-1,x,y); s=tr[x].size; ans=getval(x,s); root=merg(x,y); return ans; } int getnex( int v){ //找后继 int x,y,ans; split(root,v,x,y); ans=getval(y,1); root=merg(x,y); return ans; } int main(){ int n,op,v; scanf ( "%d" ,&n); for ( int i=1;i<=n;i++){ scanf ( "%d %d" ,&op,&v); if (op==1) inser(v); else if (op==2) del(v); else if (op==3) printf ( "%d\n" ,getrank(v)); else if (op==4) printf ( "%d\n" ,getval(root,v)); else if (op==5) printf ( "%d\n" ,getpre(v)); else printf ( "%d\n" ,getnex(v)); } return 0; } |
文艺平衡树
还是有旋转操作的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | // Luogu P3391 【模板】文艺平衡树 #include <iostream> using namespace std; const int N=100010; struct node{ int l,r; //左右儿子 int val; //树的权值 int key; //堆的随机值 int size; //子树大小 int tag; //懒标记 }tr[N]; int n,m,root,idx; int newnode( int v){ tr[++idx].val=v; tr[idx].key= rand (); tr[idx].size=1; return idx; } void pushup( int p){ tr[p].size=tr[tr[p].l].size +tr[tr[p].r].size+1; } void pushdown( int p){ if (!tr[p].tag||!p) return ; swap(tr[p].l, tr[p].r); tr[tr[p].l].tag ^= 1; tr[tr[p].r].tag ^= 1; tr[p].tag = 0; } void split( int p, int k, int &x, int &y){ if (!p) {x=y=0; return ;} pushdown(p); if (k>tr[tr[p].l].size){ k-=tr[tr[p].l].size+1; x=p; split(tr[p].r,k,tr[p].r,y); } else { y=p; split(tr[p].l,k,x,tr[p].l); } pushup(p); } int merge( int x, int y){ if (!x||!y) return x+y; if (tr[x].key<tr[y].key){ pushdown(x); tr[x].r=merge(tr[x].r,y); pushup(x); return x; } else { pushdown(y); tr[y].l=merge(x,tr[y].l); pushup(y); return y; } } void reverse( int l, int r){ int x,y,z; split(root,r,x,z); split(x,l-1,x,y); tr[y].tag ^= 1; //标记 root=merge(merge(x,y),z); } void output( int p){ if (!p) return ; pushdown(p); output(tr[p].l); printf ( "%d " ,tr[p].val); output(tr[p].r); } int main(){ srand ( time (0)); scanf ( "%d%d" ,&n,&m); for ( int i=1;i<=n;i++) root=merge(root,newnode(i)); for ( int x,y,i=1;i<=m;i++){ scanf ( "%d%d" ,&x,&y); reverse(x, y); } output(root); return 0; } |
删除:如果有两个子节点,找到优先级大的,把x向反方向旋转,也就是把x向树的下层调整,直到旋转到叶子节点
!很多题目涉及名次树
常用操作:
struct node,旋转rotate,插入insert(),查找第k大的数kth(),查询某个数find()【名次树】
少林 hdu 4585
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | #include <bits/stdc++.h> using namespace std; using ll = long long ; const ll inf = 4e18+10; const int mod = 1000000007; const int mx = 5e6+5; //check the limits, dummy typedef pair< int , int > pa; const double PI = acos (-1); ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; } #define swa(a,b) a^=b^=a^=b #define re(i,a,b) for(int i=(a),_=(b);i<_;i++) #define rb(i,a,b) for(int i=(b),_=(a);i>=_;i--) #define clr(a) memset(a, 0, sizeof(a)) #define lowbit(x) ((x)&(x-1)) #define mkp make_pair void sc( int & x) { scanf ( "%d" , &x); } void sc(int64_t& x) { scanf ( "%lld" , &x); } void sc( double & x) { scanf ( "%lf" , &x); } void sc( char & x) { scanf ( " %c" , &x); } void sc( char * x) { scanf ( "%s" , x); } int m, n,k,sum=0,ans=0,t; int id[mx]; struct node { int size; //以这个节点为根的子树的节点总数,用于名次树 int rank; //优先级 int key; //键值 node *son[2]; //son[0]左儿子,son[1]右儿子 bool operator<( const node& a) const { return rank < a.rank;} int cmp( int x) const { if (x == key) return -1; return x < key ? 0 : 1; } void update() { size = 1; if (son[0] != NULL)size += son[0]->size; if (son[1] != NULL)size += son[1]->size; } }; void rotate(node* &o, int d) { //d=0,左旋,d=1,右旋 node *k = o->son[d ^ 1]; //d^1等价于1-d,但是更快 o->son[d ^ 1] = k->son[d]; k->son[d] = o; o->update(); k->update(); o = k; } void insert(node* &o, int x) { //把x插入树中 if (o == NULL) { o = new node(); o->son[0] = o->son[1] = NULL; o->rank = rand (); o->key = x; o->size = 1; } else { int d = o->cmp(x); insert(o->son[d],x); o->update(); if (o < o->son[d]); rotate(o, d ^ 1); } } int kth(node* o, int k) { //返回第k大的数 if (o == NULL || k <= 0 || k > o->size) return -1; int s = o->son[1] == NULL ? 0 : o->son[1]->size; if (k == s + 1) return o->key; else if (k <= s) return kth(o->son[1], k); else return kth(o->son[0], k - s - 1); } int find(node* o, int k) { //返回元素k的名次 if (o == NULL) return -1; int d = o->cmp(k); if (d == -1) return o->son[1] == NULL ? 1 : o->son[1]->size + 1; else if (d == 1) return find(o->son[d],k); else { int tmp = find(o->son[d], k); if (tmp == -1) return -1; else { return o->son[1] == NULL ? tmp + 1 : tmp + 1 + o ->son[1]->size; } } } int main() { ios::sync_with_stdio( false ); cin.tie(0); cout.tie(0); while (cin>>n&&n) { srand ( time (NULL)); int k, g; cin >> k >> g; node* root = new node(); root->son[0] = root-> son[1] = NULL; root->rank = rand (); root->key = g; root->size = 1; id[g] = k; cout << k << ' ' << 1<<endl; re(i, 2, n + 1) { cin >> k >> g; id[g] = k; insert(root, g); t = find(root, g); //返回新和尚的名次 int ans1, ans2; ans1 = kth(root, t - 1); //前一名的老和尚 ans2 = kth(root, t + 1); //后一名的老和尚 if (ans1 != -1 && ans2 != -1) { ans = ans1 - g >= g - ans2 ? ans2 : ans1; } else if (ans1 == -1) ans = ans2; else ans = ans1; cout << k << ' ' << id[ans] << endl; } } return 0; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
2020-06-03 公平组合游戏
2020-06-03 概论和数学期望
2020-06-03 组合数学