并查集概述
并查集是一种树型的数据结构,用于处理不相交集合的合并及查询问题。
并查集主要分为两种操作:查找和合并。
(1)查找元素的祖先,为降低复杂度,首先进行路径压缩,即找到最久远的祖先时“顺便"把它的子孙直接连接到他的孩子处,这样就避免了树的结构退化,提高效率。
int getFather(int u){
if(father[u]!=u
father[u]=getFather(father[u]);
return father[u];
}
(2)将两个不相交的集合合并,类似于两个数的合并。
void Union(int x,int y){
int fx=getFather(x),fy=getFather(y);
if(fx!=fy){
father[fx]=fy;
}
}
1、POJ 1988 Cube Stacking
题目链接:https://vjudge.net/problem/POJ-1988
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 30010
int n,fa[N],r[N],mx[N];
//r[x]表示x到根节点的距离,mx[x]表示这个集合中有多少个盒子
void init(){
int i;
for(i=1;i<N;i++)
fa[i]=i,r[i]=0,mx[i]=1;
}
int find(int x){ //查找并查集的祖先
int fx=fa[x];
if(fa[x]!=x){//自己是自己的祖先即是根
fx=find(fa[x]);
r[x]+=r[fa[x]];
}
return fa[x]=fx;
}
void U(int x,int y){ //将两个集合合并
int fx=find(x),fy=find(y);
fa[fy]=fx;
r[fy]+=mx[fx]; //合并时更改相应值
mx[fx]+=mx[fy];
}
int main(){
char op[3];
int i,j;
while(scanf("%d",&n)!=-1){
init();
while(n--){
scanf("%s",op);
if(op[0]=='C'){
scanf("%d",&i);
int f=find(i);
printf("%d\n",mx[f]-r[i]-1); //该集合盒子个数减去在i盒子之上的个数
}
else{
scanf("%d%d",&i,&j);
U(i,j);
}
}
}
return 0;
}
POJ1182 食物链(还不太懂)
题目链接:
https://vjudge.net/problem/POJ-1182
参考:
https://www.cnblogs.com/zhuanzhuruyi/p/5863738.html
https://blog.csdn.net/qq_39520417/article/details/81569982
/*
不同树合并且更新关系(x树做主根),如果 x和y为关系r1, y和z为关系r2, 那么x和z的关系就是(r1+r2)%3
如果 d1则x和y是同类 ,那么 y对x的关系是0,如果d2 ,则x吃了y, 那么y对x的关系是1, x对y的关系是2。综上所述 ,无论d为1或者是为2, y对x的关系都是 d-1。
fy对y 的关系为 3-r[y] (有点互补的感觉,注意这里是不同类喔)
y对x的关系为 d-1,
x 对fx 的关系为 r[x]
所以fy对fx 的关系是(3-r[y] + d-1 + r[x])%3。
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 50010
int fat[maxn];//存父节点
int ran[maxn];//存与父节点的关系,0同一类,1被父节点吃,2吃父节点
void init(int n){
for(int i=0;i<=n;i++){
fat[i]=i;
ran[i]=0;
}
return;
}
int find(int x){
if(x==fat[x])
return fat[x];
int y=find(fat[x]);
ran[x]=(ran[x]+ran[fat[x]])%3;//递归后从祖先节点向后到每个孩子来计算
return fat[x]=y;//路径压缩
}
int U(int d,int x,int y){//区间合并与查询
int fx=find(x),fy=find(y);
if(fx==fy){//共父节点才能判断出关系
if((ran[x]-ran[y]+3)%3==d-1)
return 0;
return 1;
}
fat[fx]=fy;//连接两父节点
ran[fx]=(-ran[x]+d-1+ran[y]+3) %3;//注意处理负数情况
return 0;
}
int main(){
int n,k,a,x,y;
scanf("%d %d",&n,&k);
init(n);
int ans=0;
while(k--){
scanf("%d %d %d",&a,&x,&y);
if(x==y&&a==2)ans++;
else if(x>n||y>n)ans++;
else
ans+=U(a,x,y);
}
printf("%d\n",ans);
return 0;
}