zoukankan      html  css  js  c++  java
  • 【ybt金牌导航4-5-1】【luogu P3369】普通平衡树(替罪羊树做法)

    普通平衡树

    题目链接:ybt金牌导航4-5-1 / luogu P3369

    题目大意

    平衡树模板题,要求维护一些操作。
    插入一个数,删除一个数,查询一个数的排名,查询排名一直的数,找前驱后继。

    思路

    这个其实是模板题,可以有各种解法。
    现在在学替罪羊树,就用替罪羊树来做了。

    后面有空的话应该会弄一个平衡树合集。

    替罪羊其实就是弄一个限度,如果对于一个点,它的某个子树的点的个数占了这个点对应子树点个数的一定量的时候,就需要暴力重构它对应的树,把它的深度弄小。
    弄成什么最小呢?没错,就是尽可能弄成每个点两个子树的个数相同的情况。
    对于看是否要重构的那个量,我们一般会设 (0.6sim0.7)

    然后就来看看如何暴力重构。
    首先,它就要两个子树大小相同,那假设你还原出来数组,那就是每次折半,前面的给左子树,后面的给右子树。
    那怎么找到子树呢?我们可以用中序遍历,因为平衡树有二叉搜索树的性质。
    然后你再按先序遍历把重构的树里面的点都找出来,然后你每次折半找子树的时候,就拿出一个来作为这个子树的根。

    然后其他操作就很暴力,就不多说了,看代码吧。

    代码

    //替罪羊
    
    #include<cstdio>
    #define alpha 0.7
    
    using namespace std;
    
    struct Tree {
    	int l, r, val, fa, num, sum;
    }tree[1000001];
    struct reTree {
    	int val, num;
    }retree[100001];
    int n, op, x, root, sta[100001];
    int maxn, tot, tott;
    
    bool check(int x) {//判断是否失衡
    	if (!tree[x].num) return 0;
    	if (tree[tree[x].l].sum >= (double)(alpha * tree[x].sum)) return 1;
    	if (tree[tree[x].r].sum >= (double)(alpha * tree[x].sum)) return 1;
    	return 0;
    }
    
    void up(int now) {//向上传递值
    	tree[now].sum = tree[now].num;
    	if (tree[now].l) tree[now].sum += tree[tree[now].l].sum;
    	if (tree[now].r) tree[now].sum += tree[tree[now].r].sum;
    }
    
    void dfs(int x) {//dfs把树拍扁,好重构
    	sta[++sta[0]] = x;//记录点的编号
    	if (tree[x].l) dfs(tree[x].l);
    	if (tree[x].num) {//按中序遍历记录值
    		retree[++tott].val = tree[x].val;
    		retree[tott].num = tree[x].num;
    	}
    	tree[x].sum = 0;
    	if (tree[x].r) dfs(tree[x].r);
    }
    
    int get_new() {//把你拍扁的点取出来
    	if (sta[0]) {
    		sta[0]--;
    		return sta[sta[0] + 1];
    	}
    	else return ++tot;
    }
    
    void make_new(int l, int r, int now, int father) {//重构一个最平衡的树
    	int mid = (l + r) >> 1;
    	tree[now].fa = father;
    	tree[now].num = retree[mid].num;
    	tree[now].val = retree[mid].val;
    	tree[now].sum = 1;
    	if (l <= mid - 1) {
    		tree[now].l = get_new();
    		make_new(l, mid - 1, tree[now].l, now);
    	}
    	else tree[now].l = 0;
    	if (r >= mid + 1) {
    		tree[now].r = get_new();
    		make_new(mid + 1, r, tree[now].r, now);
    	}
    	else tree[now].r = 0;
    	up(now);
    }
    
    void rebuild(int x) {//暴力重构树
    	if (!x) return ;
    	tott = 0;
    	dfs(x);//拍扁,搞粗中序遍历
    	int now = get_new();
    	if (x == root) root = now;
    	tree[now].fa = tree[x].fa;
    	int mid = (1 + tott) >> 1;//先把要重构的树的根弄了
    	if (retree[mid].val < tree[tree[now].fa].val) tree[tree[now].fa].l = now;
    		else tree[tree[now].fa].r = now;
    	make_new(1, tott, now, tree[now].fa);
    }
    
    void insert(int x, bool need_build) {//插入点
    	if (root == 0) {
    		root = 1;
    		tot = 1;
    		tree[1] = (Tree){0, 0, x, 0, tree[1].num + 1, tree[1].sum + 1};
    		return ;
    	}
    	
    	int now = root;
    	while (tree[now].sum) {//一直跑跑到插入的位置
    		if (x == tree[now].val) {//之前出现过,直接加个数
    			tree[now].num++;
    			tree[now].sum++;
    			return ;
    		}
    		if (!tree[now].l && x < tree[now].val) break;//新的边,要重新建
    		if (!tree[now].r && x > tree[now].val) break;
    		tree[now].sum++;
    		if (x < tree[now].val) now = tree[now].l;
    			else if (x > tree[now].val) now = tree[now].r;
    	}
    	if (x == tree[now].val) {//之前出现过,直接加个数
    		tree[now].num++;
    		tree[now].sum++;
    		return ;
    	}
    	
    	tree[now].sum++;
    	
    	int tmp = now;//建立新的点,并且完善值和它父亲的儿子的值
    	if (x < tree[now].val) now = tree[now].l = ++tot;
    		else if (x > tree[now].val) now = tree[now].r = ++tot;
    	tree[now].num++;
    	tree[now].sum++;
    	tree[now].val = x;
    	if (tmp != now) tree[now].fa = tmp;
    	
    	int first_fix = 0;//判断树是否需要重构
    	while (now != root) {
    		now = tree[now].fa;
    		if (check(now)) first_fix = now;
    	}
    	if (need_build && first_fix) rebuild(first_fix);
    }
    
    int tree_place(int x) {//查找一个值在树上的位置
    	int now = root;
    	while (tree[now].val != x && tree[now].sum != 0) {
    		if (x <= tree[now].val) now = tree[now].l;
    			else now = tree[now].r;
    	}
    	return now;
    }
    
    void delete_(int x, int need_build) {//删去一个点
    	tree[x].num--;
    	tree[x].sum--;
    	int first_fix = 0;//判断是否需要重构
    	while (x != root) {
    		x = tree[x].fa;
    		tree[x].sum--;
    		if (check(x)) first_fix = x;
    	}
    	
    	if (need_build && first_fix) rebuild(first_fix);
    }
    
    int get_rank(int x) {//得到排名
        insert(x, 0);//因为待会会删掉,所以不用重构
    	int now = tree_place(x);
    	int re = tree[tree[now].l].sum + 1;
    	while (now != root) {
    		if (tree[tree[now].fa].r == now)
    			re += tree[tree[tree[now].fa].l].sum + tree[tree[now].fa].num;
    		now = tree[now].fa;
    	}
    	delete_(tree_place(x), 0);
    	return re;
    }
    
    int get_num(int x) {//得到排名对于的值
    	int now = root;
    	while (x) {
    		if (x <= tree[tree[now].l].sum) now = tree[now].l;
    			else if (x <= tree[tree[now].l].sum + tree[now].num) return tree[now].val;
    				else {
    					x -= tree[tree[now].l].sum + tree[now].num;
    					now = tree[now].r;
    				}
    	}
    	return tree[now].fa;
    }
    
    int pre(int x) {//前驱
    	insert(x, 0);
    	int need_rank = get_rank(x) - 1;
    	delete_(tree_place(x), 0);
    	return get_num(need_rank);
    }
    
    int suc(int x) {//后继
    	insert(x + 1, 0);
    	int need_rank = get_rank(x + 1);
    	delete_(tree_place(x + 1), 0);
    	return get_num(need_rank);
    }
    
    int main() {
    	scanf("%d", &n);
    	
    	for (int i = 1; i <= n; i++) {
    		scanf("%d %d", &op, &x);
    		
    
    		if (op == 1) {
    			insert(x, 1);
    			continue;
    		}
    		if (op == 2) {
    			delete_(tree_place(x), 1);
    			continue;
    		}
    		if (op == 3) {
    			printf("%d
    ", get_rank(x));
    			continue;
    		}
    		if (op == 4) {
    			printf("%d
    ", get_num(x));
    			continue;
    		}
    		if (op == 5) {
    			printf("%d
    ", pre(x));
    			continue;
    		}
    		if (op == 6) {
    			printf("%d
    ", suc(x));
    			continue;
    		}
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    推荐两个在线格式化JavaScript代码的网站
    JavaScript 获取未知对象的属性
    笔记本“电源已接通,未充电”的解决办法
    一个好的网站: StudioStyles
    Vertical Text with CSS(用CSS竖向排列文本)
    TSQL 行列互换
    获得enum中的枚举值
    恶搞百度
    想买个洗衣机,吓出了一身水……
    一个获取SQL Server数据库连接字符串的简单方法
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_4-5-1.html
Copyright © 2011-2022 走看看