海之音
INFOR 36th 學術長 @小海_夢想特急_夢城前
Data Structure
Definition
暑假資讀 語法 & STL
取最值
一些special case
什麼是樹
根
葉子
父子關係
Linked List
struct Linked_list {
struct node {
int data;
node *left, *right;
} *front = nullptr, *back = nullptr;
int size = 0;
}
插入 借我偷下以前的圖
插入
插入
插入
插入
刪除
刪除
刪除
Segment Tree
實作樹形結構:類似鄰接串列
指標指向左子節點、右子節點
扣
struct Stree {
struct node {
int data;
node *lchild = nullptr, *rchild = nullptr;
} *root = nullptr;
int merge(int data1, int data2);
void build(int l, int r, node *cur) {
if (l == r - 1) return;
int m = (l + r) / 2; // 也可以是 (l + r + 1) / 2 ,個人喜歡這個
cur->lchild = new node, build(l, m, cur->lchild); // 遞迴左子樹
cur->rchild = new node, build(m, r, cur->rchild); // 遞迴右子樹
cur->data = merge(cur->lchild->data, cur->rchild->data);
return;
}
};
設遞迴區間大小
設處理大小為 的區間需要 的時間
不難發現
扣
struct Stree {
int query(int l, int r, int tl, int tr, node *cur) { // l, r 表示在樹裡面的區間,tl, tr 表示需要處理的區間
if (l == tl && r == tr) return cur->data;
int m = (l + r) / 2;
if (tr <= m) return query(l, m, tl, tr, cur->lchild); // 如果目標區間完全在左子節點範圍,向左遞迴
if (tl >= m) return query(m, r, tl, tr, cur->rchild); // 如果目標區間完全在右子節點範圍,向右遞迴
return merge(query(l, m, tl, m, cur->lchild),
query(m, r, m, tr, cur->rchild)); // 否則將當前區間切割,向左右遞迴
}
};
設處理目標區間對應到的線段樹區間大小為 ,且處理的目標區間至少有一界符合線段樹區間的左右界
設處理時間
處理兩界都不符合則時間 ,但只發生一次,總複雜度仍
struct Stree {
void modify(int l, int r, int pos, int new_data, node *cur) {
if (l == r - 1) {
cur->data = new_data;
return;
}
int m = (l + r) / 2;
if (pos < m) modify(l, m, pos, new_data, cur->lchild);
else modify(m, r, pos, new_data, cur->rchild);
cur->data = merge(cur->lchild->data, cur->rchild->data);
return;
}
};
struct Stree {
int merge_data(int data1, int data2);
int merge_tags(int tag1, int tag2);
int pull(int l, int r, node* cur);
void modify(int l, int r, int tl, int tr, int tag, node *cur) {
if (l == tl && r == tr) {
cur->tag = merge_tags(tag, cur->tag);
return;
}
int m = (l + r) / 2;
if (tr <= m) modify(l, m, tl, tr, tag, cur->lchild);
else if (tl >= m) modify(m, r, tl, tr, tag, cur->rchild);
else modify(l, m, tl, m, tag, cur->lchild),
modify(m, r, m, tr, tag, cur->rchild);
pull(l, r, cur);
}
};
這樣查詢會是好的嗎?
扣(附查詢)
struct Stree {
int merge_tag_data(int data, int tag, int l, int r);
void push(node *cur, int l, int r) {
cur->data = merge_tag_data(cur->data, cur->tag, l, r);
cur->lchild->tag = merge_tags(cur->tag, cur->lchild->tag);
cur->rchild->tag = merge_tags(cur->tag, cur->rchild->tag);
cur->tag = 0;
}
int query(int l, int r, int tl, int tr, node *cur) {
push(cur, l, r);
if (l == tl && r == tr) return cur->data;
int m = (l + r) / 2;
if (tr <= m) return query(l, m, tl, tr, cur->lchild);
if (tl >= m) return query(m, r, tl, tr, cur->rchild);
return merge_data(query(l, m, tl, m, cur->lchild),
query(m, r, m, tr, cur->rchild));
}
};
以 {1, 2, 7} 找第 2 大為例:
大概的扣
const int max_n = 2e5 + 1;
const int max_q = 2e5 + 1;
int n, q;
// 建議用偽指標寫,我用指標型只是因為我懶
struct Stree {
struct node {
int count = 0; // 在這個區間裡有多少數字
int l, r; // 這個區間維護的 l, r
node *lchild = nullptr, *rchild = nullptr;
} *root[max_n];
int cur_root = 0;
void build(node *cur) { // 建藍色部分
if (cur->l + 1 == cur->r) return;
int m = (cur->l + cur->r + 1) / 2;
cur->lchild = new node, cur->lchild->l = cur->l, cur->lchild->r = m;
build(cur->lchild);
cur->rchild = new node, cur->rchild->l = m, cur->rchild->r = cur->r;
build(cur->rchild);
}
node *new_root() {
return root[++cur_root] = new node;
}
void add_num(node *pre, node *cur, int num) {
cur->l = pre->l, cur->r = pre->r, cur->count = pre->count + 1;
if (cur->l + 1 == cur->r) return;
int m = (cur->l + cur->r + 1) / 2;
if (num < m) {
cur->lchild = new node, cur->rchild = pre->rchild;
add_num(pre->lchild, cur->lchild, num);
}
else {
cur->lchild = pre->lchild, cur->rchild = new node;
add_num(pre->rchild, cur->rchild, num);
}
}
} intv_cnt; // interval_count
void solve() {
int C; // 值域
// init n, q
int data[n];
// init data
intv_cnt.root[0] = new Stree::node;
intv_cnt.root[0]->l = 0, intv_cnt.root[0]->r = C;
intv_cnt.build(intv_cnt.root[0]);
for (int i = 0; i < n; i++) {
intv_cnt.add_num(intv_cnt.root[intv_cnt.cur_root], intv_cnt.new_root(), data[i]);
}
for (int i = 0; i < q; i++) {
int l, r, k;
// get l, r, k
--l, --r; // 我們要求的區間一般是左閉右閉,先讓 l 轉成要扣掉的部分
Stree::node *lcur = intv_cnt.root[l], *rcur = intv_cnt.root[r];
while (lcur->l + 1 != lcur->r) {
if (rcur->count - lcur->count <= k) lcur = lcur->lchild, rcur = rcur->rchild;
else lcur = lcur->rchild, rcur = rcur->rchild, k -= rcur->count - lcur->count;
}
// print lcur->l
}
}
Binary Indexed Tree
struct BIT {
int tree[n + 1];
int lowbit(int n) {
return n & -n;
}
void build() {
for (int i = 1; i < n; i++) {
tree[i] += data[i];
if (i + lowbit(i) <= n) tree[i + lowbit(i)] += tree[i];
}
}
}
看看建樹的扣,會發現會影響到的區間有
x + lowbit(x), x + lowbit(x) + lowbit(x + lowbit(x)) ...
:阿那這樣為什麼不用線段樹
:它碼短 它常數小
全部的扣
真的很短
struct BIT {
int tree[max_n + 1];
void build() {
for (int i = 1; i <= n; i++) {
tree[i] += data[i];
if (i + (i & -i) <= n) tree[i + (i & -i)] += tree[i];
}
}
void modify(int pos, int diff) {
for (; pos <= n; pos += pos & -pos) tree[pos] += diff;
}
int query(int pos) {
int ans = 0;
for (; pos > 0; pos -= pos & -pos) ans += tree[pos];
return ans;
}
};
Project 參考題解
雖然我知道大家都用普通 DP
#include <stdio.h>
#include <algorithm>
#include <utility>
const int max_n = 2e5 + 1;
struct BIT {
long long tree[max_n << 1];
int size;
inline void modify(int pos, long long n) {
for (; pos <= size; pos += pos & -pos) tree[pos] = std::max(tree[pos], n);
}
inline long long query(int pos) {
if (!pos) return 0;
long long ans = 0;
for (; pos > 0; pos -= pos & -pos) ans = std::max(ans, tree[pos]);
return ans;
}
} max_money;
struct lisan {
int data[max_n << 1];
int size;
inline void init() {
std::sort(data, data + size);
size = std::unique(data, data + size) - data;
}
inline int operator[](int real) {
return std::lower_bound(data, data + size, real) - data;
}
} map;
bool cmp(const std::pair<std::pair<int, int>, int> _A, const std::pair<std::pair<int, int>, int> _B) {
return _A.first.first < _B.first.first;
}
std::pair<std::pair<int, int>, int> date[max_n];
#define begin_i (date[i].first.first)
#define end_i (date[i].first.second)
#define reward_i (date[i].second)
int main() {
int n;
scanf("%d", &n);
map.size = n << 1;
max_money.size = n << 1;
for (int i = 0; i < n; i++) {
scanf("%d%d%lld", &begin_i, &end_i, &reward_i);
map.data[i << 1] = begin_i;
map.data[i << 1 | 1] = end_i;
}
std::sort(date, date + n, cmp);
map.init();
for (int i = 0; i < n; i++) {
max_money.modify(map[end_i] + 1, max_money.query(map[begin_i]) + reward_i);
}
printf("%lld\n", max_money.query(n << 1));
}
Heap
shift down
shift down
shift down
shift down
struct Heap {
int tree[max_n * 2 + 1];
int size = 0;
void shift_down(int pos) {
while(tree[pos] > tree[pos * 2] ||
tree[pos] > tree[pos * 2 + 1]) {
if (tree[pos * 2] < tree[pos * 2 + 1]) {
swap(tree[pos], tree[pos * 2]);
pos = pos * 2;
}
else {
swap(tree[pos], tree[pos * 2 + 1]);
pos = pos * 2 + 1;
}
}
}
};
tip:把不會用到的地方通通設成不會動到的值然後把陣列開大,方便處理邊界情況以及 Debug
也可以不把陣列開大,但值建議還是要設
build
struct Heap {
static const int INF = INT_MAX;
void init(int _size, int data[]) {
size = _size;
for (int i = 0; i < size; i++) tree[i + 1] = data[i];
for (int i = size; i <= max_n * 2; i++) tree[i] = INF;
}
void build() {
for (int i = size; i > 0; i--) shift_down(i);
}
};
struct Heap {
void shift_up(int pos) {
while(pos > 1 && tree[pos / 2] > tree[pos])
swap(tree[pos/2], tree[pos]);
}
void push(int x) {
tree[++size] = x;
shift_up(size);
}
};
push & shift up
struct Heap {
void pop() {
tree[1] = tree[size];
tree[size] = INF;
--size;
shift_down(1);
}
int top() {
return tree[1];
}
};
pop & top
一般來說比賽很少要手刻堆
所以你可以挑幾題 priotity queue 的題來做一做
但基本上沒必要 所以我沒放
Binary Search Tree / Treap
扣
struct BST {
struct node {
int val;
node *lchild = nullptr, *rchild = nullptr;
} *root = nullptr;
node *&find(int _val) {
if (!root) return root;
node *cur = root, *par = nullptr;
while (cur->val != _val) {
par = cur;
if (_val < cur->val) {
if (cur->lchild) cur = cur->lchild;
else return cur->lchild;
} else {
if (cur->rchild) cur = cur->rchild;
else return cur->rchild;
}
}
return (par->lchild->val == _val) ? par->lchild : par->rchild;
}
void insert(int _val) {
node *&target = find(_val);
if (!target) {
node *new_node = new node;
new_node->val = _val;
target = new_node;
}
}
};
Case 1: 沒子節點
Case 1: 沒子節點
Case 2: 一個子節點
Case 2: 一個子節點
Case 2: 一個子節點
Case 3: 兩個子節點
Case 3: 兩個子節點
Case 3: 兩個子節點
轉化成一個子節點/沒有子節點的情況
扣
tip: 當需要取用到不存在的節點資料時,可以實作NIL節點
struct BST {
void remove(int _val) {
node *&target = find(_val);
if (!target) return;
if (target->lchild && target->rchild) {
node *alt = target->rchild, *par = target;
while (alt->lchild) par = alt, alt = alt->lchild;
target->val = alt->val;
if (par == target) target->rchild = alt->rchild;
else par->lchild = alt->rchild;
delete alt;
return;
}
node *temp = target;
if (target->lchild) target = target->lchild;
else if (target->rchild) target = target->rchild;
delete temp;
return;
}
};
因為是二分搜所以可以直接套
嗎?
不合法樹堆(因插入66)
白:val
黃:pri
檢查插入和父節點
旋轉
檢查新根和父節點
旋轉
std::mt19937 mt_rand(std::random_device{}());
參考題解
iscoj 陣列不能開太大,最好用 vector
有些部分是我在耍毒可以忽略
// 不能壓常哭哭
#include <stdlib.h>
#include <time.h>
#include <iostream>
#include <string>
#include <vector>
#define io std::ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using std::cin;
using std::cout;
using std::string;
using std::vector;
using ll = long long;
const int max_n = 5e5 + 1;
const ll mod = 1e9 + 7;
const int INF = 2147483647;
// O(n) 做完後均攤是O(1)的,雖然我猜常數壓小點用O(log n)的快速冪也會過
vector<ll> pow27;
void init_pow() {
pow27[0] = 1LL;
for (int i = 1; i <= max_n; i++) pow27.push_back(pow27[i - 1] * 27 % mod);
}
// 樹堆,維護最小堆
struct Treap {
struct Node {
int pri, size = 1; // 權值,子樹大小
ll hash = 0;
char val = 0;
Node *lchild, *rchild;
};
inline static Node *NIL, *root;
Treap() {
NIL = new Node;
root = NIL;
NIL->size = 0;
NIL->pri = INF;
NIL->lchild = NIL;
NIL->rchild = NIL;
}
void update(Node *cur) {
if (cur != NIL) {
cur->size = cur->lchild->size + cur->rchild->size + 1;
cur->hash = (cur->lchild->hash + cur->val * pow27[cur->lchild->size] + cur->rchild->hash * pow27[cur->lchild->size + 1]) % mod;
}
return;
}
void lrotate(Node *&cur) {
Node *new_root = cur->rchild;
cur->rchild = cur->rchild->lchild;
update(cur);
new_root->lchild = cur;
cur = new_root;
update(cur);
}
void rrotate(Node *&cur) {
Node *new_root = cur->lchild;
cur->lchild = cur->lchild->rchild;
update(cur);
new_root->rchild = cur;
cur = new_root;
update(cur);
}
void insert(char val, int pos, Node *&cur = root) {
if (pos - cur->lchild->size - 1 >= 0) {
if (cur->rchild != NIL) insert(val, pos - cur->lchild->size - 1, cur->rchild);
else {
cur->rchild = new Node;
cur->rchild->pri = rand();
cur->rchild->hash = val - 'a' + 1;
cur->rchild->val = val - 'a' + 1;
cur->rchild->lchild = NIL;
cur->rchild->rchild = NIL;
}
} else {
if (cur->lchild != NIL) insert(val, pos, cur->lchild);
else {
cur->lchild = new Node;
cur->lchild->pri = rand();
cur->lchild->hash = val - 'a' + 1;
cur->lchild->val = val - 'a' + 1;
cur->lchild->lchild = NIL;
cur->lchild->rchild = NIL;
}
}
// 檢查樹平衡
// 可以保證,每次至多只有一邊是不合法的
// 如果左邊不合法,則右旋轉
if (cur->lchild->pri < cur->pri) rrotate(cur);
// 如果右邊不合法,則左旋轉
else if (cur->rchild->pri < cur->pri) lrotate(cur);
else update(cur);
return;
}
void update_to_leaf(Node *&cur) {
if (cur->lchild != NIL && cur->rchild != NIL) {
if (cur->lchild->pri < cur->rchild->pri) {
rrotate(cur);
update_to_leaf(cur->rchild);
} else {
lrotate(cur);
update_to_leaf(cur->lchild);
}
update(cur);
return;
} else if (cur->lchild != NIL) {
Node *new_root = cur->lchild;
delete cur;
cur = new_root;
return;
} else if (cur->rchild != NIL) {
Node *new_root = cur->rchild;
delete cur;
cur = new_root;
return;
} else {
delete cur;
cur = NIL;
return;
}
}
void remove(int pos, Node *&cur = root) {
// find
if (pos - cur->lchild->size - 1 == 0) {
update_to_leaf(cur);
update(cur);
return;
}
if (pos - cur->lchild->size - 1 > 0) remove(pos - cur->lchild->size - 1, cur->rchild);
else remove(pos, cur->lchild);
update(cur);
return;
}
} dynamic_string;
int main() {
io;
srand(time(NULL));
pow27.push_back(1LL);
init_pow();
string S;
cin >> S;
for (int i = 0; i < S.size(); i++) dynamic_string.insert(S[i], i);
int Q;
cin >> Q;
while (Q--) {
int type, x;
char c;
cin >> type;
if (type == 1) {
cin >> x >> c;
dynamic_string.insert(c, x);
} else if (type == 2) {
cin >> x;
dynamic_string.remove(x);
} else {
cout << dynamic_string.root->hash << '\n';
}
}
return 0;
}
這是扣
我開始後悔教旋轉式樹堆了
直觀但難寫得要死
struct BST {
struct node {
int val, pri;
node *lchild = nullptr, *rchild = nullptr;
} *root;
void pull(node *cur);
void split(node *cur, int k, node *&A, node *&B) {
if (!cur) {
A = B = nullptr;
return;
}
if (cur->val <= k) {
A = cur;
split(cur->rchild, k, A->rchild, B);
} else {
B = cur;
split(cur->lchild, k, A, B->lchild);
}
pull(cur);
}
node *merge(node *A, node *B) {
if (!A || !B) {
if (A) return A;
if (B) return B;
return nullptr;
}
if (A->pri < B->pri) {
A->rchild = merge(A->rchild, B);
pull(A);
return A;
} else {
B->lchild = merge(A, B->lchild);
pull(B);
return B;
}
}
};
struct BST {
void insert(int k) {
node *L = nullptr, *R = nullptr;
node *new_node;
new_node->val = k, new_node->pri = rand();
split(root, k, L, R);
root = merge(merge(L, new_node), R);
}
void remove(int k) {
node *L = nullptr, *M = nullptr, *R = nullptr;
split(root, k - 1, L, M), split(M, k, M, R);
delete M;
root = merge(L, R);
}
};
扣
我真的恨透了我自己
再寫一次剛剛的樹堆題
Sparse Table
const int lg_n = std::__lg(n) + 1;
const int INF = 2147483647;
for (int i = 0; i < n; i++) scanf("%d", &sparse_table[0][i]);
for (int i = 1; i < lg_n; i++) {
int j = 0;
for (; j + (1 << (i - 1)) < n; j++)
sparse_table[i][j] = min(sparse_table[i - 1][j], sparse_table[i - 1][j + (1 << (i - 1))]);
for (; j < n; j++) sparse_table[i][j] = INF;
}
Iterative Segment Tree
參考資料
zkw 線段樹和 Codeforces 上那一篇講的不一樣,我主要採用 Codeforces 上的版本,畢竟左閉右開而且好理解(大喜)
扣(以區間和為例)
懶標是類似的,但可以只開 size 大小,因為葉節點不用存懶標
struct Stree {
int size;
vector<int> tree;
void build(vector<int>& data) {
size = data.size();
tree.resize(size << 1);
for (int i = 0; i < size; i++) tree[i + size] = data[i];
for (int i = size - 1; i > 0; i--) tree[i] = tree[i << 1] + tree[i << 1 | 1];
}
void modify(int pos, int new_data) {
pos += size;
tree[pos] = new_data;
for (; pos > 0; pos >>= 1) tree[pos >> 1] = tree[pos] + tree[pos ^ 1];
}
int query(int l, int r) {
int ans = 0;
for (l += size, r += size; l < r; l >>= 1, r >>= 1) {
if (l & 1) ans += tree[l++];
if (r & 1) ans += tree[--r];
}
return ans;
}
};
push & pull
struct Stree {
int size;
vector<int> tree, tag;
void pull(int v) {
v += size;
for (int node_size = 1; v > 0; v >>= 1, node_size <<= 1) tree[v >> 1] = tree[v] + tag[v] * node_size + tree[v ^ 1] + tag[v ^ 1] * node_size;
}
void push(int v) {
for (int h = std::__lg(v + size); h >= 0; h--) {
tag[v >> h] += tag[v >> h >> 1];
tag[v >> h ^ 1] += tag[v >> h >> 1];
tag[v >> h >> 1] = 0;
}
pull(v);
}
};
前面除了持久化和動態開點的題目都能用迭代式寫
Hash Table
Red Black Tree
#include <functional>
#include <utility>
#pragma GCC optimize(3)
/**
* @file rb_tree.cpp
* @brief This is a template of rb_tree.
* Use array instead of pointer.
* Can be used for map or set, multiset, etc.
* @author Sea of Voices, CKHS INFOR 36th.
* @date 2023/07/29
*/
using std::pair;
template <typename _Key, typename _Mapped, typename _Cmp = std::less<_Key>>
struct Rb_tree {
#define RED 1
#define BLACK 0
#define NIL 0
#define LEFT 0
#define RIGHT 1
private:
/**
* @brief Surely, the node of a tree.
* sub_size represents the size of left subtree + right subtree + 1
*/
struct Node {
public:
bool color = RED;
_Key key;
_Mapped value;
int parent = 0, lchild = 0, rchild = 0;
unsigned int sub_size = 1;
Node() {}
};
/**
* @brief Use array instead of using "new"
*/
struct _Alloc {
public:
inline static const int max_size = 200005, delete_size = 10;
inline static Node data[max_size];
inline static int space[delete_size];
int back = 1, stack_back = 0;
inline int new_node() {
return stack_back ? back++ : space[--stack_back];
}
inline int new_node(const _Key& _key, int _parent) {
if (!stack_back) {
data[back].key = _key;
data[back].parent = _parent;
return back++;
}
--stack_back;
data[space[stack_back]].key = _key;
data[space[stack_back]].parent = _parent;
return space[stack_back];
}
inline void relese_space(int _pos) {
data[_pos].parent = 0, data[_pos].lchild = 0, data[_pos].rchild = 0;
data[_pos].color = RED;
data[_pos].sub_size = 1;
space[stack_back++] = _pos;
return;
}
};
private:
inline static _Alloc alloc;
inline static int root = 0, _size = 0;
_Cmp cmp;
public:
/**
* @brief A simple iterator of the tree.
* Provides a bridge between other functions and value.
*/
struct iterator {
private:
int pos = 0;
public:
inline iterator() {}
inline iterator(int _pos) {
pos = _pos;
return;
}
_Mapped& operator*() {
return alloc.data[pos].value;
}
bool operator==(const iterator& it) {
return it.pos == pos;
}
bool operator!=(const iterator& it) {
return it.pos != pos;
}
iterator& operator++() {
#define Self alloc.data[pos]
#define is_lchild (alloc.data[alloc.data[pos].parent].lchild == pos)
if (pos == NIL) {
pos = root;
while (Self.lchild != NIL) pos = Self.lchild;
} else if (Self.rchild != NIL) {
pos = Self.rchild;
while (Self.lchild != NIL) pos = Self.lchild;
} else if (is_lchild) {
pos = Self.parent;
} else {
while (!is_lchild && pos != NIL) pos = Self.parent;
if (pos != NIL) pos = Self.parent;
}
return *this;
#undef Self
#undef is_lchild
}
iterator& operator=(const iterator& _it) {
pos = _it.pos;
}
};
public:
// default constructor
Rb_tree() {
alloc.data[0].color = BLACK;
alloc.data[0].sub_size = 0;
cmp = _Cmp{};
}
private:
/**
* @brief This function rotates a subtree.
* rotate_root stands for the root of the subtree,
* LEFT(0) stands for left rotation, RIGHT(1) stands
* for right rotation.
*/
inline void rotate(int rotate_root, bool direction) {
#define Parent alloc.data[alloc.data[rotate_root].parent]
#define Self alloc.data[rotate_root]
#define Rchild alloc.data[alloc.data[rotate_root].rchild]
#define Lchild alloc.data[alloc.data[rotate_root].lchild]
if (direction == LEFT) {
if (Self.parent != NIL) {
if (Parent.lchild == rotate_root) Parent.lchild = Self.rchild;
else Parent.rchild = Self.rchild;
} else root = Self.rchild;
Rchild.parent = Self.parent;
Self.parent = Self.rchild;
Self.rchild = Rchild.lchild;
if (Self.rchild != NIL) Rchild.parent = rotate_root;
Parent.lchild = rotate_root;
Self.sub_size = Lchild.sub_size + Rchild.sub_size + 1;
Parent.sub_size = Self.sub_size + alloc.data[Parent.rchild].sub_size + 1;
} else {
if (Self.parent != NIL) {
if (Parent.lchild == rotate_root) Parent.lchild = Self.lchild;
else Parent.rchild = Self.lchild;
} else root = Self.lchild;
Lchild.parent = Self.parent;
Self.parent = Self.lchild;
Self.lchild = Lchild.rchild;
if (Self.rchild != NIL) Lchild.parent = rotate_root;
Parent.rchild = rotate_root;
Self.sub_size = Lchild.sub_size + Rchild.sub_size + 1;
Parent.sub_size = Self.sub_size + alloc.data[Parent.lchild].sub_size + 1;
}
return;
#undef Parent
#undef Self
#undef Rchild
#undef Lchild
}
private:
/**
* @brief After inserting a node, update the shape of this tree.
*/
inline void update_node(int v) {
// update node
#define Self alloc.data[v]
#define Parent alloc.data[alloc.data[v].parent]
#define Grand_parent alloc.data[alloc.data[alloc.data[v].parent].parent]
#define is_parent_left_child (alloc.data[alloc.data[alloc.data[v].parent].parent].lchild == alloc.data[v].parent)
#define is_self_right_child (alloc.data[alloc.data[v].parent].rchild == v)
// update the size on the chain
for (int update_node = Self.parent; update_node != NIL; update_node = alloc.data[update_node].parent) ++alloc.data[update_node].sub_size;
while (v != root) {
// case 2: parent is black
if (Parent.color == BLACK) {
return;
}
// case 3: uncle is red
if (alloc.data[Grand_parent.lchild].color == RED && alloc.data[Grand_parent.rchild].color == RED) {
alloc.data[Grand_parent.lchild].color = BLACK;
alloc.data[Grand_parent.rchild].color = BLACK;
Grand_parent.color = RED;
v = Parent.parent;
continue; // back to case 1
}
// case 4: if it's a "zap" structure
if (is_parent_left_child == is_self_right_child) v = Self.parent, rotate(v, is_self_right_child ? RIGHT : LEFT);
// case 5: it's a chain
Parent.color = BLACK;
Grand_parent.color = RED;
rotate(Parent.parent, is_parent_left_child ? RIGHT : LEFT);
break;
}
if (v == root) Self.color = BLACK;
return;
#undef Self
#undef Parent
#undef Grand_parent
#undef is_parent_left_child
#undef is_self_right_child
}
private:
/**
* @brief This function insert a nod without change the value of it.
* Returns {pointer, success or not}
*/
inline pair<int, bool> _insert(const _Key& _key) {
if (!root) {
_size++;
root = alloc.new_node(_key, 0);
alloc.data[root].color = BLACK;
return {1, 1};
}
// binary search, insert
int v = root;
#define Self alloc.data[v]
while (v) {
if (Self.key == _key) return {v, 0};
if (cmp(_key, Self.key)) {
if (Self.lchild != NIL) v = Self.lchild;
else {
v = Self.lchild = alloc.new_node(_key, v);
break;
}
} else {
if (Self.rchild != NIL) v = Self.rchild;
else {
v = Self.rchild = alloc.new_node(_key, v);
break;
}
}
}
_size++;
return {v, 1};
#undef Self
}
public:
/**
* @brief Insert a key with value.
* If there exist a same key, it changes nothing but returns {irerator, 0}.
* Otherwise, it returns {iterator, 1}
*/
inline pair<iterator, bool> insert(const _Key& _key, const _Mapped& _val) {
pair<int, bool> result = _insert(_key);
alloc.data[result.first].value = _val;
if (result.second) update_node(result.first);
iterator result_it(result.first);
return {result_it, result.second};
}
/**
* @brief Returns the size of the tree.
*/
inline unsigned int size() {
return _size;
}
public:
/**
* @brief insert if there is no same key, update value if there exist a key.
*/
inline _Mapped& operator[](const _Key& _key) {
pair<int, bool> result = _insert(_key);
if (result.second) update_node(result.first);
return alloc.data[result.first].value;
}
public:
/**
* @brief Returns an iterator that point which is empty node, the end of the tree.
* Time complexity: O(1)
*/
inline iterator end() {
iterator result;
return result;
}
public:
/**
* @brief Returns an itrator that point at the start of the tree.
* It's the leftest node of the tree.
* WARNING, Different from STL, its time complexity is O(log n) (while STL is constant)
*/
inline iterator begin() {
iterator result;
return ++result;
}
public:
/**
* @brief Find with a key, and returns an iterator.
* Returns end() if it doesn't found anything.
*/
inline iterator find(const _Key& _key) {
#define Self alloc.data[v]
if (!root) return end();
int v = root;
while (v != NIL) {
if (_key == Self.key) break;
v = cmp(_key, Self.key) ? Self.lchild : Self.rchild;
}
iterator result(v);
return result;
#undef Self
}
private:
/**
* @brief This gives a way to erase a node without updating whole tree.
*/
int _erase(const _Key& _key) {
#define Target alloc.data[target]
#define Target_parent alloc.data[alloc.data[target].parent]
// find the target node
if (!root) return -1;
int target = root;
while (target != NIL) {
if (_key == Target.key) break;
target = cmp(_key, Target.key) ? Target.lchild : Target.rchild;
}
if (target == NIL) return -1;
--_size;
#define Self alloc.data[remove]
#define Parent alloc.data[alloc.data[remove].parent]
#define Lchild alloc.data[alloc.data[remove].lchild]
#define Rchild alloc.data[alloc.data[remove].rchild]
#define is_lchild (alloc.data[alloc.data[remove].parent].lchild == remove)
#define Empty alloc.data[0]
int remove = target;
if (Self.lchild != NIL && Self.rchild != NIL) {
remove = Self.rchild;
while (Self.lchild != NIL) remove = Self.lchild;
Target.key = Self.key;
Target.value = Self.value;
}
for (int v = remove; v != NIL; v = alloc.data[v].parent) --alloc.data[v].sub_size;
// focus on "Self"
if (Self.parent != NIL) {
if (is_lchild) Parent.lchild = Self.lchild != NIL ? Self.lchild : Self.rchild;
else Parent.rchild = Self.lchild != NIL ? Self.lchild : Self.rchild;
}
// delete the root
else {
root = Self.lchild != NIL ? Self.lchild : Self.rchild;
alloc.data[root].color = BLACK;
alloc.data[root].parent = NIL;
alloc.relese_space(remove);
return -1;
}
if (Self.lchild != NIL) Lchild.parent = Self.parent, Self.color = Lchild.color;
else if (Self.rchild != NIL) Rchild.parent = Self.parent, Self.color = Rchild.color;
else Empty.parent = Self.parent, alloc.relese_space(remove);
if (Self.color == RED || Lchild.color == RED || Rchild.color == RED) {
Lchild.color = BLACK;
Rchild.color = BLACK;
alloc.relese_space(remove);
return -1;
}
return remove;
#undef Self
#undef Parent
#undef Lchild
#undef Rchild
#undef is_lchild
#undef Empty
}
private:
/**
* @brief Update when a node is removed.
*/
void update_removed_node(int v) {
#define Self alloc.data[v]
#define Parent alloc.data[alloc.data[v].parent]
#define Lchild alloc.data[alloc.data[v].lchild]
#define Rchild alloc.data[alloc.data[v].rchild]
#define is_left (alloc.data[alloc.data[v].parent].lchild == v)
#define Empty alloc.data[0]
int remove = v;
while (v != root) {
if (is_left) {
#define Brother alloc.data[alloc.data[alloc.data[v].parent].rchild]
// case 1
if (Brother.color == RED) {
Parent.color = RED;
Brother.color = BLACK;
rotate(Self.parent, LEFT);
}
// case 2
if (alloc.data[Brother.lchild].color == BLACK && alloc.data[Brother.rchild].color == BLACK) {
v = Self.parent;
Rchild.color = RED;
if (Self.color == RED) {
Self.color = BLACK;
break;
}
continue;
}
// case 3
if (alloc.data[Brother.rchild].color == BLACK) {
alloc.data[Brother.lchild].color = RED;
rotate(Parent.rchild, RIGHT);
}
// case 4
Brother.color = Parent.color;
Parent.color = BLACK;
alloc.data[Brother.rchild].color = BLACK;
rotate(Self.parent, LEFT);
break;
}
#undef Brother
#define Brother alloc.data[alloc.data[alloc.data[v].parent].lchild]
else {
// case 1
if (Brother.color == RED) {
Parent.color = RED;
Brother.color = BLACK;
rotate(Self.parent, RIGHT);
}
// case 2
if (alloc.data[Brother.lchild].color == BLACK && alloc.data[Brother.rchild].color == BLACK) {
v = Self.parent;
Rchild.color = RED;
if (Self.color == RED) {
Self.color = BLACK;
break;
}
continue;
}
// case 3
if (alloc.data[Brother.lchild].color == BLACK) {
alloc.data[Brother.rchild].color = RED;
rotate(Parent.lchild, LEFT);
}
// case 4
Brother.color = Parent.color;
Parent.color = BLACK;
alloc.data[Brother.lchild].color = BLACK;
rotate(Self.parent, RIGHT);
break;
}
break;
}
alloc.relese_space(remove);
Empty.parent = NIL;
#undef Brother
#undef Self
#undef Parent
#undef Lchild
#undef Rchild
#undef is_left
#undef Empty
}
public:
/**
* @brief Removes a node from the tree.
*/
void erase(const _Key& _key) {
int result = _erase(_key);
if (result != -1) update_removed_node(result);
return;
}
public:
/**
* @brief Find a node with its order in this map.
* If cannot find the node, returns end().
*/
iterator find_by_order(int _order) {
#define Self alloc.data[v]
#define Lchild alloc.data[alloc.data[v].lchild]
int cur_order = 0;
int v = root;
while (v != NIL) {
if (cur_order + Lchild.sub_size == _order) break;
if (cur_order + Lchild.sub_size > _order) v = Self.lchild;
else cur_order += Lchild.sub_size + 1, v = Self.rchild;
}
iterator result(v);
return v;
#undef Self
#undef Lchild
}
public:
/**
* @brief Find a node with its key, return its order in this map.
*/
int order_of_key(const _Key& _key) {
#define Self alloc.data[v]
#define Lchild alloc.data[alloc.data[v].lchild]
int cur_order = 0;
int v = root;
while (v != NIL) {
if (_key == Self.key) return cur_order + Lchild.sub_size;
if (cmp(_key, Self.key)) {
if (Self.lchild != NIL) v = Self.lchild;
else return cur_order;
} else {
cur_order += Lchild.sub_size + 1;
if (Self.rchild != NIL) v = Self.rchild;
else return cur_order;
}
}
return -1;
#undef Self
#undef Lchild
}
#undef RED
#undef BLACK
#undef LEFT
#undef RIGHT
#undef NIL
};
未完工的模版 (527行)
主要應該是刪除寫爛了
扣很醜,如果你真的有興趣可以來修修看
不難發現根到葉最長路徑不大於最短路徑的兩倍
可以證明樹高是 的
注意到可以使用迭代實現,不用遞迴
在節點裡要多記錄父節點的指標
善用酷酷的參考,你會寫得開心很多
還有可以發現樹旋轉必定 <= 2 次,所以常數很小
假定 current 是左子節點,如果是右節點操作要對稱過去
By 海之音
資結,指關於資訊的心結,關於某題校內賽被卡狀態數和常數的心結。