并查集是一种群众喜闻乐见的数据结构,其复杂度是数据结构中最奇葩的之一了,Tarjan证明其为阿克曼函数的反函数,在可以想象(不全面的解释啊)的范围内小于等于3。。。我们就把它当做O(1)吧。下面通过几道基础的并查集来探查一下并查集的应用。递归调用并查集。
最裸的并查集就只有表示一个集合的功能,支持动态的合并,查询两者是否属于一个集合,这部分内容太简单,请自行Baidu。
在并查集上可以加入边权,成为加权并查集,一般来说这类题目形式比较单一,纯属个人娱乐吧。。
加权并查集裸题——银河英雄传说
这是一道最裸的加权并查集,对于每一个节点维护一个到最老最先的边权值和,再在祖先上维护一个Size,表示集合的大小。Wikioi上的题解有个水表说不能路径压缩。。。怎么可能嘛!!!我们仔细思考一下,每一个点在一个集合中只会被压缩一次,压缩之后就会被连接到祖先上,到祖先的权值和等于到原来的父亲的权值加上原来的父亲到祖先的权值和,只需要在压缩的时候更新一下就好了。合并的时候,把两个集合(不妨设A,B)合并在一起,假如把A并入B,则把A的祖先节点连在B的祖先上,边权设为B的Size。相当于把A集合直接接在B的后面,如此,这个问题就被很好的解决了。
代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <algorithm> 5 6 using namespace std; 7 struct spaceship 8 { 9 int father; 10 int len,maxlen; 11 12 spaceship(){len=0;maxlen=1;} 13 }sp[30010]; 14 15 int get(int n) 16 { 17 if (sp[n].father==n) return n; 18 int m=get(sp[n].father); 19 sp[n].len+=sp[sp[n].father].len; 20 sp[n].father=m; 21 return sp[n].father; 22 } 23 24 void marry(int n,int m) 25 { 26 int nn=get(n); 27 int mm=get(m); 28 sp[nn].father=mm; 29 sp[nn].len+=sp[mm].maxlen; 30 sp[mm].maxlen+=sp[nn].maxlen; 31 } 32 33 int main() 34 { 35 int n; 36 scanf("%d",&n); 37 for (int i=1;i<=30000;i++) sp[i].father=i; 38 for (int i=1;i<=n;i++) 39 { 40 char ch; 41 int j,k; 42 char s[5]; 43 scanf("%s",s); 44 if (s[0]=='M') 45 { 46 scanf("%d%d",&j,&k); 47 marry(j,k); 48 } 49 else 50 { 51 scanf("%d%d",&j,&k); 52 if (get(j)==get(k)) 53 { 54 printf("%d\n",abs(sp[j].len-sp[k].len)-1); 55 } 56 else 57 printf("-1\n"); 58 } 59 } 60 return 0; 61 }