一,并查集的用途
*并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。
例如:1,合并两个集合,复杂度是O(1)。
2,查询一个元素在哪个集合里面,复杂度O(1)。
3,查询两个元素是否在同一个集合里面。
二,并查集建立的主要步骤:
1,初始化:每个结点的父亲结点首先设为它本身。
void init(){
for(int i=1;i<=n;i++)fa[i]=i,total[i]=1;
//注意有需要时才加total[i]数组,total数组仅是用来计数的。
}
2,路径压缩(解决特殊情况下的树的层次深而造成的复杂度增大的问题)
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
3,合并
void Merge(int a,int b){
int p1=find(a),p2=find(b);
if(p1!=p2)fa[p2]=p1;
//当需要计数时代码需改为:if(p1!=p2)fa[p2]=p1,total[p1]+=total[p2];
}
三,并查集注意事项
1,前期未经过路径压缩的树形结构并查集,树的深度有可能是n。
未经过路径压缩的并查集find()函数(获得父亲结点的):
int find(int x){return fa[x]==x?x:find(fa[x]);}
例如:
2,并查集一定要记得初始化。
3,有些并查集的题目用cin,cout有可能会超时。遇到常数比较大的尽量不要用cin,cout输入输出。
四,例题:
1.*看题目请戳这里poj 1611 The Suspects此题是一个模板题
AC code:
#include<iostream>
using namespace std;
const int N=3e4+6;
int n,m,k,fa[N],total[N];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void Merge(int a,int b){
int p1=find(a),p2=find(b);
if(p1!=p2)total[p1]+=total[p2],fa[p2]=p1;
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
while(cin>>n>>m,n||m){
for(int i=0;i<n;i++)fa[i]=i,total[i]=1;
for(int i=0;i<m;i++){
int h,s;cin>>k>>h;
for(int j=1;j<k;j++){
cin>>s;Merge(h,s);
}
}
cout<<total[find(0)]<<endl;
}
}
2,看题请戳这里hdu1232
AC code:
#include<iostream>
using namespace std;
const int N=1e3+6;
int n,m,ans,fa[N];
int find(int x){return fa[x]==-1?x:fa[x]=find(fa[x]);}
void Merge(int a,int b){
int p1=find(a),p2=find(b);
if(p1!=p2)fa[p1]=p2;
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
while(cin>>n>>m,n){ans=0;
for(int i=1;i<=n;i++)fa[i]=-1;
for(int i=1;i<=m;i++){
int a,b;cin>>a>>b;
Merge(a,b);
}
for(int i=1;i<=n;i++)
if(fa[i]==-1)ans++;
cout<<ans-1<<endl;
}
}
3,模板微变形。poj 1988 堆箱子
题意:给你P个操作,有两种可能,一是移动操作,移动操作就是把X砖的这一堆放到Y砖这一堆的上面。其次是统计操作,统计X砖下面一共有多少砖。
AC code:
#include<iostream>
using namespace std;
const int N=1e5+6;
int fa[N],under[N],sum[N];
int find(int x){
if(fa[x]==x)return x;
int t=find(fa[x]);
under[x]+=under[fa[x]];
fa[x]=t;return fa[x];
}
void Merge(int a,int b){
int p1=find(a),p2=find(b);
if(p1!=p2)fa[p1]=p2,under[p1]=sum[p2],sum[p2]+=sum[p1];
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int T;cin>>T;
for(int i=1;i<=N-1;i++){fa[i]=i;under[i]=0;sum[i]=1;}
while(T--){
char ch;int a,b,c;cin>>ch;
if(ch=='M'){cin>>a>>b;Merge(a,b);}
else {cin>>c;find(c);cout<<under[c]<<endl;}
}
}
4,并查集的拓展应用:poj 1182 食物链
*题意:
输入输出:
思路:
AC code:
#include<cstdio>
const int N=15e5+6;
int n,k,T[N],X[N],Y[N],fa[N],ans;
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void Merge(int a,int b){
int p1=find(a),p2=find(b);
if(p1!=p2)fa[p2]=p1;
}
void solve(){
for(int i=0;i<=n*3;i++)fa[i]=i;
for(int i=0;i<k;i++){
int t=T[i];int x=X[i]-1,y=Y[i]-1;
if(x<0||n<=x||y<0||n<=y){ans++;continue;}
if(t==1){
if(find(x)==find(y+n)||find(x)==find(y+2*n))ans++;
else {
Merge(x,y);
Merge(x+n,y+n);
Merge(x+2*n,y+2*n);
}
}
else
if(find(x)==find(y)||find(x)==find(y+2*n))ans++;
else {
Merge(x,y+n);
Merge(x+n,y+2*n);
Merge(x+2*n,y);
}
}
printf("%d\n",ans);
}
int main(){
scanf("%d%d",&n,&k);
for(int i=0;i<k;i++)scanf("%d%d%d",T+i,X+i,Y+i);
solve();
}