SGT替罪羊树模板

由于要动态后缀数组需要用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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HeartFireY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值