平衡树专题
1.普通Treap
通过左右旋来维护堆的性质
左右旋是不改变中序遍历的
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cstdio>
#include<cmath>
#include<ctime>
using namespace std;
const int MAXN = 100100, inf = 0x7fffffff;
struct Node{
int ch[2];
int val, prio;
int cnt, siz;
// Node(){ch[0] = ch[1] = val = cnt = siz = 0;}
}t[MAXN];
int n;
int root, pool_cur;
void init();
int newnode(int val);
void delnode(int cur);
void pushup(int cur);
void rotate(int &cur, int d);
void Insert(int &cur, int val);
void Remove(int &cur, int val);
int getpre(int val);
int getnxt(int val);
int getrankbyval(int cur, int val);
int getvalbyrank(int cur, int rank);
int main() {
srand(time(NULL));
scanf("%d", &n);
int opt, x;
init();
for(int i = 1; i <= n; ++i) {
scanf("%d%d", &opt, &x);
switch(opt) {
case 1:{
Insert(root, x);
break;
}
case 2:{
Remove(root, x);
break;
}
case 3:{
printf("%d\n", getrankbyval(root, x) - 1);
break;
}
case 4:{
printf("%d\n", getvalbyrank(root, x + 1));
break;
}
case 5:{
printf("%d\n", getpre(x));
break;
}
case 6:{
printf("%d\n", getnxt(x));
break;
}
}
}
return 0;
}
void init() {
newnode(-inf);
newnode(inf);
root = 1;
t[1].ch[1] = 2;
pushup(root);
return;
}
int newnode(int val) {
int cur = ++pool_cur;
memset(t + cur, 0, sizeof(Node));
t[cur].siz = t[cur].cnt = 1;
t[cur].prio = rand();
t[cur].val = val;
return cur;
}
void pushup(int cur) {
t[cur].siz = t[t[cur].ch[0]].siz + t[t[cur].ch[1]].siz + t[cur].cnt;
return;
}
void rotate(int &cur, int d) {
int u = t[cur].ch[d];
t[cur].ch[d] = t[u].ch[d^1];
t[u].ch[d^1] = cur;
t[u].siz = t[cur].siz;
pushup(cur);
cur = u;
return;
}
void Insert(int &cur, int val) {
if(cur == 0) {
cur = newnode(val);
return;
}
if(t[cur].val == val) {
++t[cur].cnt;
pushup(cur);
return;
}
int d = t[cur].val < val;
Insert(t[cur].ch[d], val);
pushup(cur);
if(t[t[cur].ch[d]].prio < t[cur].prio) rotate(cur, d);
return;
}
void Remove(int &cur, int val) {
if(!cur) return;
if(t[cur].val == val) {
int o = cur;
if(t[cur].cnt > 1) {
--t[cur].cnt;
} else {
if(!t[cur].ch[0]) {
cur = t[cur].ch[1];
} else if(!t[cur].ch[1]) {
cur = t[cur].ch[0];
} else {
int d = t[t[cur].ch[0]].prio < t[t[cur].ch[1]].prio;
rotate(cur, d ^ 1);
Remove(t[cur].ch[d], val);
}
}
pushup(cur);
} else {
int d = t[cur].val < val;
Remove(t[cur].ch[d], val);
}
pushup(cur);
return;
}
int getpre(int val) {
int ans = 1;
int cur = root;
while(cur) {
if(val == t[cur].val) {
if(t[cur].ch[0] > 0) {
cur = t[cur].ch[0];
while(t[cur].ch[1] > 0) cur = t[cur].ch[1];
ans = cur;
}
break;
}
if(t[cur].val < val and t[cur].val > t[ans].val) ans = cur;
cur = t[cur].val > val ? t[cur].ch[0] : t[cur].ch[1];
}
return t[ans].val;
}
int getnxt(int val) {
int ans = 2;
int cur = root;
while(cur) {
if(val == t[cur].val) {
if(t[cur].ch[1] > 0) {
cur = t[cur].ch[1];
while(t[cur].ch[0] > 0) cur = t[cur].ch[0];
ans = cur;
}
break;
}
if(t[cur].val > val and t[cur].val < t[ans].val) ans = cur;
cur = t[cur].val > val ? t[cur].ch[0] : t[cur].ch[1];
}
return t[ans].val;
}
int getrankbyval(int cur, int val) {
if(!cur) return 0;
if(val == t[cur].val) return t[t[cur].ch[0]].siz + 1;
return t[cur].val > val ? getrankbyval(t[cur].ch[0], val) : (getrankbyval(t[cur].ch[1], val) + t[t[cur].ch[0]].siz + t[cur].cnt);
}
int getvalbyrank(int cur, int rank) {
if(!cur) return 0;
if(t[t[cur].ch[0]].siz >= rank) return getvalbyrank(t[cur].ch[0], rank);
if(t[t[cur].ch[0]].siz + t[cur].cnt >= rank) return t[cur].val;
return getvalbyrank(t[cur].ch[1], rank - t[t[cur].ch[0]].siz - t[cur].cnt);
}
2.fhq_Treap (非旋 Treap )
通过 Split 和 Merge 操作来维护平衡树
Split :
把以 cur 为根的树的前 k 个元素分离开
返回值为分离后的两根
pair<int, int> Split(int cur, int k) {
if(!cur or !k) return make_pair(0, cur);
pair<int, int> res;
if(t[lson].siz >= k) {
res = Split(lson, k);
lson = res.second;
pushup(cur);
res.second = cur;
} else {
res = Split(rson, k - t[lson].siz - 1);
rson = res.first;
pushup(cur);
res.first = cur;
}
return res;
}
进入右子树,则一定选当前节点,将它作为分离后左边那棵树的根
进入左子树,则一定不选当前节点,将它作文分离后的右边那棵树的根
Merge :
像可并堆那样进行合并
这样来满足堆性质
int Merge(int x, int y) {
if(!x) return y; if(!y) return x;
if(t[x].prio < t[y].prio) {
t[x].ch[1] = Merge(t[x].ch[1], y);
pushup(x);
return x;
} else {
t[y].ch[0] = Merge(x, t[y].ch[0]);
pushup(y);
return y;
}
}
这里写一种比较清奇的 getrank , 返回所有小于 val 的元素个数
很好用的
int getrnk(int cur, int val) {
if(!cur) return 0;
return val <= t[cur].val ? getrnk(lson, val) : (getrnk(rson, val) + t[lson].siz + 1);
}
加哨兵总是挂,最后就没加 = =
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cstdio>
#include<cmath>
#include<ctime>
#define lson t[cur].ch[0]
#define rson t[cur].ch[1]
using namespace std;
const int MAXN = 100001;
struct Node{
int ch[2], siz, prio, val;
Node(){ch[0] = ch[1] = siz = val = 0;}
}t[MAXN];
int n, Root, poolcur;
inline void pushup(int cur) {
t[cur].siz = t[lson].siz + t[rson].siz + 1;
return;
}
inline int newnode(int val) {
register int cur = ++poolcur;
t[cur].val = val;
t[cur].siz = 1;
t[cur].prio = rand();
t[cur].ch[0] = t[cur].ch[1] = 0;
return cur;
}
pair<int, int> Split(int cur, int k) {
if(!cur or !k) return make_pair(0, cur);
pair<int, int> res;
if(t[lson].siz >= k) {
res = Split(lson, k);
lson = res.second;
pushup(cur);
res.second = cur;
} else {
res = Split(rson, k - t[lson].siz - 1);
rson = res.first;
pushup(cur);
res.first = cur;
}
return res;
}
int Merge(int x, int y) {
if(!x) return y; if(!y) return x;
if(t[x].prio < t[y].prio) {
t[x].ch[1] = Merge(t[x].ch[1], y);
pushup(x);
return x;
} else {
t[y].ch[0] = Merge(x, t[y].ch[0]);
pushup(y);
return y;
}
}
int getrnk(int cur, int val) {
if(!cur) return 0;
return val <= t[cur].val ? getrnk(lson, val) : (getrnk(rson, val) + t[lson].siz + 1);
}
int findkth(int k) {
pair<int, int> x = Split(Root, k - 1);
pair<int, int> y = Split(x.second, 1);
int ans = t[y.first].val;
Root = Merge(Merge(x.first, y.first), y.second);
return ans;
}
int getpre(int val) {
register int k = getrnk(Root, val);
return findkth(k);
}
int getnxt(int val) {
register int k = getrnk(Root, val + 1);
return findkth(k + 1);
}
void Insert(int val) {
pair<int, int> x = Split(Root, getrnk(Root, val));
Root = Merge(Merge(x.first, newnode(val)), x.second);
return;
}
void Remove(int val) {
register int k = getrnk(Root, val);
pair<int, int> x = Split(Root, k);
pair<int, int> y = Split(x.second, 1);
Root = Merge(x.first, y.second);
return;
}
inline int rd() {
register int x = 0;
register char c = getchar();
register bool f = false;
while(!isdigit(c)) {
if(c == '-') f = true;
c = getchar();
}
while(isdigit(c)) {
x = x * 10 + c - 48;
c = getchar();
}
return f ? -x : x;
}
int main() {
srand(time(NULL));
n = rd();
int opt, x;
while(n--) {
opt = rd(); x = rd();
switch(opt) {
case 1: {
Insert(x);
break;
}
case 2: {
Remove(x);
break;
}
case 3: {
printf("%d\n", getrnk(Root, x) + 1);
break;
}
case 4: {
printf("%d\n", findkth(x));
break;
}
case 5: {
printf("%d\n", getpre(x));
break;
}
case 6: {
printf("%d\n", getnxt(x));
break;
}
}
}
return 0;
}
(2) 用 fhq_Treap 实现区间操作
fhq_Treap是支持拼接子树的,所以也能够很好的支持区间操作
要对区间 [ l, r ] 进行操作,就先把前 r 个元素 Split 出来,再将前 l - 1 个元素 Split 出来,这样就得到了区间 [ l, r ] 的一棵Treap
之后进行想要的操作即可
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cstdio>
#include<cmath>
#include<ctime>
#define lson t[cur].ch[0]
#define rson t[cur].ch[1]
using namespace std;
const int MAXN = 100001;
struct Node{
int ch[2], siz, val, prio;
bool rvrs;
Node(){ch[0] = ch[1] = siz = val = 0; rvrs = false;}
}t[MAXN];
int n, m, Root, poolcur;
inline int rd() {
register int x = 0;
register char c = getchar();
register bool f = false;
while(!isdigit(c)) {
if(c == '-') f = true;
c = getchar();
}
while(isdigit(c)) {
x = x * 10 + c - 48;
c = getchar();
}
return f ? -x : x;
}
inline void pushup(int cur) {
t[cur].siz = t[lson].siz + t[rson].siz + 1;
return;
}
inline void pushdown(int cur) {
if(cur && t[cur].rvrs) {
t[cur].rvrs = false;
swap(lson, rson);
t[lson].rvrs ^= 1;
t[rson].rvrs ^= 1;
}
return;
}
inline int newnode(int val) {
register int cur = ++poolcur;
t[cur].val = val;
t[cur].siz = 1;
t[cur].prio = rand();
t[cur].rvrs = false;
return cur;
}
pair<int, int> Split(int cur, int k) {
if(!cur or !k) return make_pair(0, cur);
pushdown(cur); pair<int, int> res;
if(t[lson].siz >= k) {
res = Split(lson, k);
lson = res.second;
pushup(cur);
res.second = cur;
} else {
res = Split(rson, k - t[lson].siz - 1);
rson = res.first;
pushup(cur);
res.first = cur;
}
return res;
}
int Merge(int x, int y) {
pushdown(x); pushdown(y);
if(!x) return y; if(!y) return x;
if(t[x].prio < t[y].prio) {
t[x].ch[1] = Merge(t[x].ch[1], y);
pushup(x);
return x;
} else {
t[y].ch[0] = Merge(x, t[y].ch[0]);
pushup(y);
return y;
}
}
int getrnk(int cur, int val) {
if(!cur) return 0;
return val <= t[cur].val ? getrnk(lson, val) : (getrnk(rson, val) + t[lson].siz + 1);
}
void Insert(int val) {
pair<int, int> x = Split(Root, getrnk(Root, val));
Root = Merge(Merge(x.first, newnode(val)), x.second);
return;
}
void Reverse(int l, int r) {
pair<int, int> x = Split(Root, r);
pair<int, int> y = Split(x.first, l - 1);
t[y.second].rvrs ^= 1;
Root = Merge(Merge(y.first, y.second), x.second);
return;
}
void Recycle(int cur) {
if(!cur) return;
pushdown(cur);
if(lson) Recycle(lson);
if(t[cur].val > 0 and t[cur].val <= n) printf("%d ", t[cur].val);
if(rson) Recycle(rson);
return;
}
inline void init() {
t[1].val = t[1].siz = 1;
Root = 1; poolcur = 1;
t[1].prio = rand();
return;
}
int main() {
srand(time(NULL));
n = rd(); m = rd();
init();
for(int i = 2; i <= n; ++i) Insert(i);
int l, r;
while(m--) {
l = rd(); r = rd();
Reverse(l, r);
}
Recycle(Root);
putchar('\n');
return 0;
}
3.替罪羊树
选定一个平衡因子Alpha,一般取值在0.6 ~ 0.7之间,0.75也可以
满足条件 ((cur.siz * Alpha >= lson.siz) and (cur.siz * Alpha >= rson.siz))则视为以cur为根的子树平衡
怎么使一棵树平衡呢
重构!
将以cur为根的子树压成一个序列(中序遍历)
将这个序列暴力重构成一棵平衡树
再把它接回去就好了
并不是能很好的支持删除操作
所以上网找了一个
或是打上删除标记在重构的时候不计入这个元素
一些细节见注释
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cstdio>
#include<cmath>
#define lson t[cur].ch[0]
#define rson t[cur].ch[1]
using namespace std;
const int MAXN = 500001, inf = 0x7fffffff;
const double Alpha = 0.7;
struct Node{
int ch[2], val, fa, siz;
Node(){ch[0] = ch[1] = val = siz = fa = 0;}
}t[MAXN];
int n, Root, poolcur;
int rebin[MAXN], bincur;
inline int rd() {
register int x = 0;
register char c = getchar();
register bool f = false;
while(!isdigit(c)) {
if(c == '-') f = true;
c = getchar();
}
while(isdigit(c)) {
x = x * 10 + c - 48;
c = getchar();
}
return f ? -x : x;
}
inline bool Balance(int cur) {
//check balance
return (((double)t[cur].siz * Alpha >= t[lson].siz) and ((double)t[cur].siz * Alpha >= t[rson].siz));
}
inline void pushup(int cur) {
t[cur].siz = t[lson].siz + t[rson].siz + 1;
return;
}
void Recycle(int cur) {
//inorder traversal,
//store it's No. will optimize the options later.
if(lson) Recycle(lson);
rebin[++bincur] = cur;
if(rson) Recycle(rson);
return;
}
int Build(int l, int r) {
//there, 'return 0' is because maybe it used to have sons.
if(l > r) return 0;
int mid = ((l + r) >> 1), Top = rebin[mid];
t[ t[Top].ch[0] = Build(l, mid - 1) ].fa = Top;
t[ t[Top].ch[1] = Build(mid + 1, r) ].fa = Top;
pushup(Top);
return Top;
}
void Rebuild(int Top) {
//Recycle the numbers,
//build the numbers into a BST,
//link it with it's old father (two dirctions),
//check if Top is the Root.
bincur = 0; Recycle(Top);
int topfa = t[Top].fa, d = (t[ t[Top].fa ].ch[1] == Top);
int cur = Build(1, bincur);
t[ t[topfa].ch[d] = cur ].fa = topfa;
if(Top == Root) Root = cur;
return;
}
void Insert(int val) {
//find a position to insert.
//int now = Root, cur = delcur ? delpool[--delcur] : ++poolcur;
int now = Root, cur = ++poolcur;
t[cur].siz = 1; t[cur].val = val;
while(1) {
++t[now].siz;
int d = (val >= t[now].val);
if(t[now].ch[d]) now = t[now].ch[d];
else {
t[ t[now].ch[d] = cur ].fa = now;
break;
}
}
int k = 0;
for(int i = t[cur].fa; i; i = t[i].fa) if(!Balance(i)) k = i;
if(k) Rebuild(k);
}
int getrnk(int cur, int val) {
if(!cur) return 0;
return val <= t[cur].val ? getrnk(lson, val) : (getrnk(rson, val) + t[lson].siz + 1);
}
int getnum(int val) {
int cur = Root;
while(1) {
if(t[cur].val == val) return cur;
else cur = t[cur].ch[t[cur].val < val];
}
}
void Remove(int GG) {
int cur = getnum(GG);
if(lson and rson) {
int now = lson;
while(t[now].ch[1]) now = t[now].ch[1];
t[cur].val = t[now].val; cur = now;
}
int son = (lson) ? lson : rson;
int d = (t[ t[cur].fa ].ch[1] == cur);
t[ t[ t[cur].fa ].ch[d] = son ].fa = t[cur].fa;
for(int i = t[cur].fa; i; i = t[i].fa) --t[i].siz;
if(cur == Root) Root = son;
return;
}
int findkth(int cur, int k) {
if(!cur) return 0;
if(t[lson].siz >= k) return findkth(lson, k);
if(t[lson].siz + 1 == k) return t[cur].val;
return findkth(rson, k - t[lson].siz - 1);
}
int getpre(int val) {
int cur = Root, ans = -inf;
while(cur) {
if(t[cur].val < val) ans = max(ans, t[cur].val), cur = t[cur].ch[1];
else cur = t[cur].ch[0];
}
return ans;
}
int getnxt(int val) {
int cur = Root, ans = inf;
while(cur) {
if(t[cur].val > val) ans = min(ans, t[cur].val), cur = t[cur].ch[0];
else cur = t[cur].ch[1];
}
return ans;
}
inline void init() {
Root = 1;
poolcur = 2;
t[0].siz = 0;
t[1].val = -inf; t[2].val = inf;
t[1].siz = 2; t[2].siz = 1;
t[1].ch[1] = 2; t[2].fa = 1;
return;
}
int main() {
scanf("%d", &n);
int opt, x;
init();
for(int i = 1; i <= n; ++i) {
scanf("%d%d", &opt, &x);
switch(opt) {
case 1:{
Insert(x);
break;
}
case 2:{
Remove(x);
break;
}
case 3:{
printf("%d\n", getrnk(Root, x));
break;
}
case 4:{
printf("%d\n", findkth(Root, x + 1));
break;
}
case 5:{
printf("%d\n", getpre(x));
break;
}
case 6:{
printf("%d\n", getnxt(x));
break;
}
}
}
return 0;
}
禁止诸如开发者知识库/布布扣/码迷/学步园/马开东等 copy 他人博文乃至博客的网站转载
,用户转载请注明出处:https://www.cnblogs.com/xcysblog/

浙公网安备 33010602011771号