zoukankan      html  css  js  c++  java
  • 【洛谷P3224】永无乡 并查集+Splay启发式合并

    题目大意:给定 N 个点的图,点有点权,初始有一些无向边,现在有 Q 个询问,每个询问支持动态增加一条无向边连接两个不连通的点和查询第 X 个点所在的联通块中权值第 K 大的是哪个点。

    题解:学会了平衡树的启发式合并。
    以每个点建立一棵平衡树,需要加边时则合并两个点对应的平衡树,启发式的思想在于合并的时候将 size 小的平衡树信息合并到 size 大的树上。这样,对于每一个被合并的点来说,其所在的平衡树的大小必定翻倍,而最极端的话,所有的点都在一个平衡树中,size = n,因此,每个点被合并的次数不超过 (O(logn)) 次,即:新建的节点数不超过 (O(nlogn)) 个。
    需要注意的是,对于 Splay 来说,若对于以前的单个平衡树来说,根节点的父节点可以取 0,但是有 N 棵平衡树的话,每棵平衡树根节点的父节点不能相同,因此,将 tot 初始化成 N,对于第 i 个平衡树的根节点,将 i 作为其父节点,并且根节点需要手动初始化,不能 insert。
    连通性什么的直接并查集维护就好喽。

    代码如下

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=1e5+10;
    
    int n,m,q,f[maxn],mp[maxn];
    int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
    struct node{
    	#define ls(x) t[x].ch[0]
    	#define rs(x) t[x].ch[1]
    	int ch[2],fa,val,size;
    }t[maxn<<3];
    int tot,root[maxn];
    inline void pushup(int o){t[o].size=t[ls(o)].size+t[rs(o)].size+1;}
    inline bool get(int o){return o==rs(t[o].fa);}
    inline void rotate(int o){
    	int fa=t[o].fa,gfa=t[fa].fa;
    	int d1=get(o),d2=get(fa);
    	t[fa].ch[d1]=t[o].ch[d1^1],t[t[o].ch[d1^1]].fa=fa;
    	t[fa].fa=o,t[o].ch[d1^1]=fa;
    	t[gfa].ch[d2]=o,t[o].fa=gfa;
    	pushup(fa),pushup(o);
    }
    inline void splay(int o,int goal){
    	while(t[o].fa!=goal){
    		int fa=t[o].fa,gfa=t[fa].fa;
    		if(gfa!=goal)get(o)==get(fa)?rotate(fa):rotate(o);
    		rotate(o);
    	}
    	if(goal<=n)root[goal]=o;
    }
    void insert(int val,int i){
    	int o=root[i],fa=0;
    	while(o)fa=o,o=t[o].ch[t[o].val<val];
    	o=++tot;
    	if(fa)t[fa].ch[t[fa].val<val]=o;
    	t[o].fa=fa,t[o].val=val,t[o].size=1;
    	splay(o,i);
    }
    int kth(int o,int k){
    	if(k<=t[ls(o)].size)return kth(ls(o),k);
    	else if(k>t[ls(o)].size+1)return kth(rs(o),k-t[ls(o)].size-1);
    	else return t[o].val;
    }
    void dfs(int o,int i){
    	if(ls(o))dfs(ls(o),i);
    	if(rs(o))dfs(rs(o),i);
    	insert(t[o].val,i);
    }
    void merge(int a,int b){
    	if(!a||!b)return;
    	int x=find(a),y=find(b);
    	if(x==y)return;
    	if(t[root[x]].size>t[root[y]].size)swap(x,y);
    	f[x]=y;
    	dfs(root[x],y);
    }
    
    void read_and_parse(){
    	scanf("%d%d",&n,&m),tot=n;
    	for(int i=1,val;i<=n;i++){
    		scanf("%d",&val);
    		f[i]=i,mp[val]=i;
    		root[i]=++tot,t[root[i]].fa=i,t[root[i]].size=1,t[root[i]].val=val;
    	}
    	for(int i=1,a,b;i<=m;i++){
    		scanf("%d%d",&a,&b);
    		merge(a,b);
    	}
    }
    
    void solve(){
    	char opt[2];int x,y;
    	scanf("%d",&q);
    	while(q--){
    		scanf("%s%d%d",opt,&x,&y);
    		if(opt[0]=='B')merge(x,y);
    		else{
    			if(y>t[root[find(x)]].size)puts("-1");
    			else printf("%d
    ",mp[kth(root[find(x)],y)]);
    		}
    	}
    }
    
    int main(){
    	read_and_parse();
    	solve();
    	return 0;
    }
    
  • 相关阅读:
    MYSQL关于数据库的操作命令
    理解js中的原型和原型链
    float引起的高度塌陷问题
    使用webpack2.0 搭建前端项目
    jquery插件开发总结
    js中的OOP编程
    关于gulp的基本使用
    关于requireJs的学习总结
    vuex的学习总结
    事件中的target与currentTarget的区别
  • 原文地址:https://www.cnblogs.com/wzj-xhjbk/p/10466848.html
Copyright © 2011-2022 走看看