由于要动态后缀数组需要用SGT维护,因此就存个板子自用
SGT的基本思想是对不平衡的节点进行暴力重构,从而保证整个树结构的平衡。
对于插入操作,和二叉搜索树是差不多,只是最后要检查回溯时的树链上有哪棵子树违反了平衡。然后直接将其重构即可。
对于删除操作,替罪羊树的删除操作很好的体现了Lazy思想。它在删除节点时,并不是真正的删除,而是将其标记。此后所有的操作都将其无视,重构就直接丢弃,除非有插入操作将其恢复。当然我们不能无止尽地进行标记。如果被标记的节点数超过了整棵树大小的一半,我们就直接将整棵树重构,同时清除删除的节点。
平衡系数一般设为 0.75 0.75 0.75,除插入和删除,其余的操作就是在二叉搜索树的上的操作,与其它二叉搜索树基本一样。
模板对应的题就是洛谷的二叉搜索树板题:https://www.luogu.com.cn/problem/P3369
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N = 2e5 + 10;
double alpha = 0.75;
namespace SGT {
struct node {
int ls, rs;
int w, wn, s, sz, sd;
} tree[N];
int cnt, root;
//* recalculate the value of rt
void calc(int rt) {
tree[rt].s = tree[tree[rt].ls].s + tree[tree[rt].rs].s + 1;
tree[rt].sz = tree[tree[rt].ls].sz + tree[tree[rt].rs].sz + tree[rt].wn;
tree[rt].sd = tree[tree[rt].ls].sd + tree[tree[rt].rs].sd + (tree[rt].wn != 0);
}
//* determine if node k needs to be rebuild
bool can_rebuild(int rt) {
return tree[rt].wn && (alpha * tree[rt].s <= (double)max(tree[tree[rt].ls].s, tree[tree[rt].rs].s) || (double)tree[rt].sd <= alpha * tree[rt].s);
}
int ldr[N];
//* flatten the sub-tree of node rt in medium-order traversal
void rebuild_nf(int &ldc, int rt) {
if(!rt) return;
rebuild_nf(ldc, tree[rt].ls);
if(tree[rt].wn) ldr[ldc++] = rt;
rebuild_nf(ldc, tree[rt].rs); // if the current node has been deleted, then no retained it.
}
//* rebuild the part [l, r] to a binary search tree(in array ldr)
int rebuild_bd(int l, int r) {
if(l >= r) return 0;
int mid = l + r >> 1; // choose the mid value as the root to make it balanced
tree[ldr[mid]].ls = rebuild_bd(l, mid);
tree[ldr[mid]].rs = rebuild_bd(mid + 1, r);
calc(ldr[mid]);
return ldr[mid];
}
//* rebuild the sub-tree of node rt
void rebuild(int &rt) {
int ldc = 0;
rebuild_nf(ldc, rt);
rt = rebuild_bd(0, ldc);
}
//* insert a node ot the subtree of rt with value k
void insert(int &rt, int k) {
if(!rt) {
rt = ++cnt;
if(!root) root = 1;
tree[rt].w = k;
tree[rt].ls = tree[rt].rs = 0;
tree[rt].wn = tree[rt].s = tree[rt].sz = tree[rt].sd = 1;
} else {
if(tree[rt].w == k) tree[rt].wn++;
else if(tree[rt].w < k) insert(tree[rt].rs, k);
else insert(tree[rt].ls, k);
calc(rt);
if(can_rebuild(rt)) rebuild(rt);
}
}
//* delete the node with value k from the sub-tree of k
void del(int &rt, int k) {
if(!rt) return;
if(tree[rt].w == k) {
if(tree[rt].wn) tree[rt].wn--;
} else {
if(tree[rt].w < k) del(tree[rt].rs, k);
else del(tree[rt].ls, k);
}
calc(rt);
if(can_rebuild(rt)) rebuild(rt);
}
//* find the lowest ranking with a weight strictly greater than k
int upper_min(int rt, int k) {
if(!rt) return 1;
else if(tree[rt].w == k && tree[rt].wn)
return tree[tree[rt].ls].sz + tree[rt].wn + 1;
else if(tree[rt].w > k)
return upper_min(tree[rt].ls, k);
else
return tree[tree[rt].ls].sz + tree[rt].wn + upper_min(tree[rt].rs, k);
}
//* find the maximum ranking with a weight strictly less than k
int lower_max(int rt, int k) {
if(!rt) return 0;
else if(tree[rt].w == k && tree[rt].wn)
return tree[tree[rt].ls].sz;
else if(tree[rt].w < k)
return tree[tree[rt].ls].sz + tree[rt].wn + lower_max(tree[rt].rs, k);
else
return lower_max(tree[rt].ls, k);
}
//* find the exactly value of rank k
int getk(int rt, int k) {
if(!rt) return 0;
else if(tree[tree[rt].ls].sz < k && k <= tree[tree[rt].ls].sz + tree[rt].wn)
return tree[rt].w;
else if(tree[tree[rt].ls].sz + tree[rt].wn < k)
return getk(tree[rt].rs, k - tree[tree[rt].ls].sz - tree[rt].wn);
else
return getk(tree[rt].ls, k);
}
inline int find_pre(int rt, int k) {
return getk(rt, lower_max(rt, k));
}
inline int find_aft(int rt, int k) {
return getk(rt, upper_min(rt, k));
}
}
using SGT::root;
inline void solve() {
int n = 0; cin >> n;
for(int i = 1; i <= n; i++) {
int op, x; cin >> op >> x;
if(op == 1) SGT::insert(root, x);
else if(op == 2){
SGT::del(root, x);
}
else if(op == 3) {
int rnk = SGT::lower_max(root, x) + 1;
cout << rnk << endl;
} else if(op == 4) {
int k = SGT::getk(root, x);
cout << k << endl;
} else if(op == 5) {
int pre = SGT::find_pre(root, x);
cout << pre << endl;
} else if(op == 6) {
int aft = SGT::find_aft(root, x);
cout << aft << endl;
}
}
}
signed main() {
ios_base::sync_with_stdio(false), cin.tie(0);
int t = 1; // cin >> t;
while(t--) solve();
return 0;
}
2102

被折叠的 条评论
为什么被折叠?



