zoukankan      html  css  js  c++  java
  • ☆ [HNOI2012] 永无乡 「平衡树启发式合并」

    题目类型:平衡树启发式合并

    传送门:>Here<

    题意:节点可以连边(不能断边),询问任意两个节点的连通性与一个连通块中排名第(k)的节点

    解题思路

    如果不需要询问排名,那么并查集即可。如果只询问排名第一,那么左偏树即可。现在要询问排名第(k)小,就需要用平衡树来解决

    平衡树求解排名第(k)是轻而易举的,然而怎么合并两棵平衡树呢?

    启发式合并。所谓启发式合并,就是暴力合并……

    所谓启发式合并(不仅仅是平衡树),就是比较要合并的两个结构,选择较小的那一个结构,将其中节点一个一个拆下来插入到较大的那个结构中去。因此当我们合并两棵平衡树时,将(size)较小的那一颗平衡树中的节点一个一个拆下来插入到较大的那棵平衡树上。

    按什么顺序拆呢?如果每次选择根节点删除然后插入显得很愚蠢。我们尽可能优化删除的情况(插入不可能优化了吧……)——按照后序遍历的顺序来插入。这样的话当我插入一个节点时,它的左右子树肯定都已经没了,因此只需要简单地将自己删除就好了。

    Code

    /*By DennyQi 2018*/
    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    #define  r  read()
    using namespace std;
    typedef long long ll;
    const int MAXN = 100010;
    const int INF = 1061109567;
    inline int Max(const int a, const int b){ return (a > b) ? a : b; }
    inline int Min(const int a, const int b){ return (a < b) ? a : b; }
    inline int read(){
        int x = 0; int w = 1; register char c = getchar();
        for(; c ^ '-' && (c < '0' || c > '9'); c = getchar());
        if(c == '-') w = -1, c = getchar();
        for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w;
    }
    int N,M,q,A,B,x,y; char opt[10];
    int val[MAXN],bel[MAXN],rt[MAXN];
    struct Splay{
    	int ch[MAXN][2],fa[MAXN],size[MAXN];
    	inline bool rson(int f, int x){
    		return ch[f][1] == x;
    	}
    	inline void update(int x){
    		size[x] = size[ch[x][0]] + size[ch[x][1]] + 1;
    	}
    	inline void rotate(int x){
    		int f = fa[x], gf = fa[f];
    		int p = rson(f, x), q = !p;
    		if(!gf) rt[bel[x]] = x; else ch[gf][rson(gf,f)] = x;
    		fa[x] = gf;
    		ch[f][p] = ch[x][q], fa[ch[x][q]] = f;
    		ch[x][q] = f, fa[f] = x;
    		update(f), update(x);
    	}
    	inline void splay(int x, int target){
    		int f,gf;
    		while(fa[x] != target){
    			f = fa[x], gf = fa[f];
    			if(gf == target){
    				rotate(x); break;
    			}
    			if(rson(gf,f) ^ rson(f,x)) rotate(x); else rotate(f);
    			rotate(x);
    		}
    	}
    	inline void insert(int A, int x){
    		int o = rt[A];
    		while(o){
    			bool b = val[x] > val[o];
    			if(!ch[o][b]){
    				ch[o][b] = x;
    				fa[x] = o;
    				bel[x] = A;
    				size[x] = 1;
    				splay(x, 0);
    				break;
    			}
    			o = ch[o][b];
    		}
    	}
    	void HEmerge(int A, int x){
    		if(ch[x][0]) HEmerge(A, ch[x][0]);
    		if(ch[x][1]) HEmerge(A, ch[x][1]);
    		insert(A, x);
    	}
    	inline void merge(int A, int B){
    		if(size[rt[A]] < size[rt[B]]) swap(A, B);
    		HEmerge(A, rt[B]);
    		rt[B] = 0;
    	}
    	inline int query(int A, int k){
    		int o = rt[A];
    		if(size[o] < k) return -1;
    		while(o){
    			if(size[ch[o][0]] >= k){
    				o = ch[o][0];
    			}
    			else if(size[ch[o][0]] + 1 < k){
    				k -= size[ch[o][0]] + 1;
    				o = ch[o][1];
    			}
    			else{
    				return o;
    			}
    		}
    	}
    }qxz;
    int main(){
    	N = r, M = r;
    	for(int i = 1; i <= N; ++i){
    		val[i] = r;
    		rt[i] = i;
    		bel[i] = i;
    		qxz.size[i] = 1;
    	}
    	for(int i = 1; i <= M; ++i){
    		A = r, B = r;
    		if(bel[A] != bel[B]) qxz.merge(bel[A], bel[B]);
    	}
    	scanf("%d", &q);
    	while(q--){
    		scanf("%s %d %d", opt, &x, &y);
    		if(opt[0] == 'B'){
    			if(bel[x] == bel[y]) continue;
    			qxz.merge(bel[x], bel[y]);
    		}
    		else{
    			printf("%d
    ", qxz.query(bel[x], y));
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    C#操作Word (2)-- 打开&关闭Word文档
    JS input 银行卡号格式转换
    解决H5在微信浏览器或QQ浏览器修改title的问题
    CSS
    Atom 编辑器使用和学习
    php的一个小坑,输出不了json_encode
    js 组合键监听ctrl + enter
    webpack3.0 环境搭建
    css 使表格随着内容自动适应宽度
    获取input光标的x和y轴
  • 原文地址:https://www.cnblogs.com/qixingzhi/p/9656031.html
Copyright © 2011-2022 走看看