此文章根据北大暑期课件并查集写的~~详情请看此文档~~~~;
树形结构,合并以及查询都能logn?????反正很快吧~~~~
Merge(b,f); 将f所在树挂为b所在树的直接子树
Query(b,f); 简单比较b和f所在树的根节点是否相同
缺点,树结构可能太深,查询太慢,
方法一:根据树的层次进行合并 ,每个节点(元素)维护一个rank表示子树最大可能高度,较小的rank的树连到较大rank树的根部。~~~此方法略了~~~;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~我是分割线~~~~~~~~~~~~~~~~~~~~~~~~~~~~
方法二:路径压缩。
为什么rank不用了,因为根据时间复杂度的计算,rank可以省略了,路径压缩足矣。详情看课件
开设父节点指示数组Par,Par[i]代表第i个元素的父亲,若元素i是树根,则Par[i] = i;
a[i]数组用来表示父亲节点是哪一个 通过Query函数 即look函数查询x的祖先节点是哪个~~~
int look(int x)
{
while(x != a[x])
x = a[x];
return x;
}
查询x的祖先并返回
合并merge函数,合并x,y
void merge(int x, int y)
{
int fx = look(x);//查询x的祖先节点
int fy = look(y);//查询y的祖先节点
if(fx != fy){ //如果祖先节点不相同
a[fx] = fy;//那么合并到一个集合中,此时fx的祖先节点为fy
cnt[fy] +=cnt[fx];//cnt为统计祖先节点所在集合的数量,cnt[fy]为fy为祖先的集合中元素的个数
}
}
代码如下
#include
#include
#include
#include
#include
#include
using namespace std;
int a[31234];
int cnt[31234];
int look(int x)
{
while(x != a[x])
x = a[x];
return x;
}
void merge(int x, int y)
{
int fx = look(x);
int fy = look(y);
if(fx != fy){
a[fx] = fy;
cnt[fy] +=cnt[fx];
}
}
int main()
{
int n, m;
int k;
int t, c;
int i, j;
while(cin >> n >> m){
if(n == 0 && m == 0) break;
for(i = 0;i < n;i++){
a[i] = i;
cnt[i] = 1;
}
for(i = 0;i < m;i++){
cin >> k;
cin >> t;
for(j = 1;j < k;j++){
cin >> c;
merge(t,c);
}
}
cout << cnt[look(0)] << endl;//0为传染源,找到0所在的祖先,输出集合中的数量即可。
}
return 0;
}
POJ 1988 Cube stacking
此题的向下求和真是神奇啊~~~~真心佩服想出此方法的大神
题目大意:
题目共有两种操作:
M x y 表示把方块x所在的堆,拿起来,叠放到y所在的堆上
C x 表示方块下面有多少个方块
操作次数p<=100,000次,对每次C操作,输出结果
题解:
除了a数组存储父亲节点以外,还要开设sum数组:记录每一堆一共有多少个方块
若a[i] = a;,则sum[a]表示a所在堆的方块数目。
under数组,under[i]表示第i个方块下面有多少个方块。
under数组在堆合并和路径压缩的时候都要更新。
注意: (此算法的核心)
调用get(4)进行路径压缩的同时,更新under[4],under[4]等于从4到根路径上所有节点的under值之和,
路径压缩的同时,该路径上所有的under值也都更新了
代码如下: 不要用cin输入 否则TLE~~~~~~教训啊~~~~~~~~~
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN = 31234;
int a[MAXN];
int sum[MAXN];//若a[i] =i;sum[i]表示砖块i所在堆的砖块数目
int under[MAXN];//under[i]表示砖块i下面有多少块砖
int get(int x)
{
if(x != a[x]){ //获取x的根,并把a的父亲节点改为根
int t = get(a[x]);
under[x] += under[a[x]]; //更新所有走过的路径,所有路径上的under值都将得到改变
a[x] = t;//使x的父亲节点为根节点
}
return a[x];
}
void merge(int x, int y)
{
int fx = get(x);
int fy = get(y);
if(fx != fy){
a[fx] = fy;//fx的父亲节点为fy,即把fx压在fy上面
under[fx] = sum[fy];//把fx压在fy上面之后under[fx]的值即为sum[fy]的值;还有一个比较好玩的就是under[fx]在赋值前一定为0,自行考虑吧,哈啊哈哈~~~~
sum[fy] += sum[fx];//此时fy那一堆的值应该再加上fx那一堆的值
}
}
int main()
{
int p;
scanf("%d", &p);
int i;
for(int i = 1;i <= MAXN;i++){
sum[i] = 1;
under[i] = 0;
a[i] = i;
}
char s[5];
int x, y;
for(i = 0;i < p;i++){
scanf("%s", s);
if(s[0] == 'M'){
scanf("%d %d", &x, &y);
merge(x,y);
}else {
scanf("%d", &x);
get(x);//更新under[x]的值,以及所有路径上的值,可能会更新,可能更新好了,就无需更新了啦啦啦啦
printf("%d\n", under[x]);
}
}
return 0;
}
一下题目有待更新
POJ 1182POJ 2492
POJ 2524
POJ 1182
POJ 1861
POJ 1703
POJ 2236
POJ 2560
POJ 1456