BZOJ 3223 普通平衡樹 | 平衡樹模板
阿新 • • 發佈:2017-12-02
++ rank 權值線段樹 enter bool markdown 不存在 esp 旋轉
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;
typedef long long ll;
template <class T>
void read(T &x){
char c;
bool op = 0;
while(c = getchar(), c < '0' || c > '9')
if(c == '-') op = 1;
x = c - '0';
while(c = getchar(), c >= '0' && c <= '9')
x = x * 10 + c - '0';
if(op) x = -x;
}
template <class T>
void write(T x){
if(x < 0) putchar('-'), x = -x;
if (x >= 10) write(x / 10);
putchar('0' + x % 10);
}
//歡迎閱讀胡小兔的平衡樹板子 =v=
const int N = 100005;
int n, root, idx, val[N], fa[N], ls[N], rs[N], sze[N], cnt[N];
#define which(x) (ls[fa[(x)]] == (x)) //判斷x的"方向": x是左兒子還是右兒子
void upt(int x){ //update: 更新sze[x]
sze[x] = sze[ls[x]] + sze[rs[x]] + cnt[x];
}
void rotate(int x){ //如果x是左兒子則右旋,右兒子則左旋
int y = fa[x], z = fa[y], b = which(x) ? rs[x] : ls[x], dir = which(y);
which(x) ? (rs[x] = y, ls[y] = b) : (ls[x] = y, rs[y] = b);
fa[y] = x, fa[b] = y, fa[x] = z;
if(z) dir ? ls[z] = x : rs[z] = x;
upt(y), upt(x); //記得旋轉之後更新大小,由下往上更新,此時y在下而x在上
}
void splay(int x){//將x旋轉至根節點
while(fa[x]){//原則:為了盡可能使樹平衡,如果x和fa[x]方向相同則先旋轉fa再旋轉x,否則旋轉兩次x
if(fa[fa[x]]){
if(which(x) == which(fa[x])) rotate(fa[x]);
else rotate(x);
}
rotate(x);
}
root = x; //記得更新根節點
}
int find(int x){ //找到值為x的節點; 如果沒有則返回
int cur = root, last = 0;
while(cur && val[cur] != x){
last = cur;
if(x < val[cur]) cur = ls[cur];
else cur = rs[cur];
}
return cur ? cur : last;
}
int getmin(int x){ //找子樹x中最小的點的編號
while(ls[x]) x = ls[x];
return x;
}
int getmax(int x){ //找子樹x中最大的點的編號
while(rs[x]) x = rs[x];
return x;
}
void insert(int x){ //插入一個數
int cur = find(x); //找到值最相近的節點的編號
if(cur && val[cur] == x) return (void)(cnt[cur]++, sze[cur]++, splay(cur)); //如果已存在這個節點,則cnt++
val[++idx] = x, fa[idx] = cur, cnt[idx] = sze[idx] = 1;// 如果不存在這個節點,則新增一個節點
if(cur) x < val[cur] ? ls[cur] = idx : rs[cur] = idx;
splay(idx);
}
void erase(int x){
int cur = find(x);
splay(cur);
if(cnt[cur] > 1) cnt[cur]--, sze[cur]--; //如果這個值去掉一個之後還存在,則只要cnt--就好了
else if(!ls[cur] || !rs[cur]) root = ls[cur] + rs[cur], fa[root] = 0; //如果至少一個兒子為空,則讓那個兒子做根節點;如果兩個兒子均為空,則說明刪除這個點後整棵樹為空
else{
fa[ls[cur]] = 0; //讓左子樹中最大的點做根節點,右子樹做新根節點的右子樹
int u = getmax(ls[cur]);
splay(u);
rs[u] = rs[cur], fa[rs[cur]] = u;
upt(u);
}
}
int getkth(int k){ //找排名為k的數,類似權值線段樹
int cur = root;
while(cur){
if(sze[ls[cur]] >= k) cur = ls[cur];
else if(sze[ls[cur]] + cnt[cur] >= k) return val[cur];
else k -= sze[ls[cur]] + cnt[cur], cur = rs[cur];
}
return val[cur];
}
int getrank(int x){ //求x的排名
int cur = find(x);
splay(cur);
return sze[ls[cur]] + 1;
}
int getpre(int x){
int cur = find(x);
if(val[cur] < x) return val[cur];
splay(cur);
return val[getmax(ls[cur])];
}
int getnxt(int x){
int cur = find(x);
if(val[cur] > x) return val[cur];
splay(cur);
return val[getmin(rs[cur])];
}
int main(){
read(n);
while(n--){
int op, x;
read(op), read(x);
if(op == 1) insert(x);
if(op == 2) erase(x);
if(op == 3) write(getrank(x)), enter;
if(op == 4) write(getkth(x)), enter;
if(op == 5) write(getpre(x)), enter;
if(op == 6) write(getnxt(x)), enter;
}
return 0;
}
BZOJ 3223 普通平衡樹 | 平衡樹模板