1. 程式人生 > >BZOJ 3223 普通平衡樹 | 平衡樹模板

BZOJ 3223 普通平衡樹 | 平衡樹模板

++ 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 普通平衡樹 | 平衡樹模板