并查集(Disjoint Set Union,DSU)
定义:
并查集是一种用来管理元素分组情况的数据结构。
作用:
查询元素a和元素b是否属于同一组
合并元素a和元素b所在的组
优化方法:
1.路径压缩
2.添加高度属性
拓展延伸:
分组并查集
带权并查集
代码如下:
//带有路径压缩的并查集 //一句话并查集(常用) int dsu(int x){ return x==par[x]?x:(par[x]=dsu(par[x])); } //带有路径压缩和高度的并查集 //ranks[i]代表以i为根的树的最大高度,若不存在则为0 int rank[maxn]; int par[maxn]; void init(int n){ for(int i=0;i<n;i++){ par[i]=i; rank[i]=0; } } int dsu(int x){ if(par[x]==x){ return x; }else{ return par[x]=dsu(par[x]); } } void union(int x,int y){ int fx=dsu(x); int fy=dsu(y); if(fx==fy){ return; } if(rank[fx]<rank[fy]){ par[fx]=fy; }else if(rank[fx]>rank[fy]){ par[fy]=fx; }else{ par[fy]=fx; rank[fx]++; } } bool same(int x,int y){ return dsu(x)==dsu(y); } poj1182 食物链 //带权并查集解法 /* 模仿矢量计算来处理权值 当我们知道x与祖先x1的关系,y与祖先y1的关系,x与y的关系时,求x1与y1的关系时,使用矢量 计算: x1->x ->y ->y1 */ #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=50010; int par[maxn],ran[maxn]; int dsu(int x){ if(x==par[x]){ return x; }else{ int fx=dsu(par[x]); //一开始将par[x]写成了fx,一直wa //如果是fx,那么每一次都到最终的根节点,求解ran数组显然不正确 ran[x]=(ran[x]+ran[par[x]]+3)%3; par[x]=fx; return par[x]; } } int main() { int n,m; int ty,x,y,ans=0; scanf("%d%d",&n,&m); for(int i=0;i<=n;i++){ par[i]=i; ran[i]=0; } while(m--){ scanf("%d%d%d",&ty,&x,&y); if(x==y&&ty==2){ ans++; } else if(x>n||y>n){ ans++; } else{ int fx=dsu(x); int fy=dsu(y); if(fx==fy) { if((ran[x]-ran[y]+3)%3!=ty-1){ ans++; //这里如果不相等,说明这句话是错误的,continue //一开始因为没写continue,一直wa continue; } } par[fx]=fy; ran[fx]=(-ran[x]+ty-1+ran[y]+3)%3; } } printf("%d\n",ans); return 0; } //分组并查集解法 /* 个人理解带权并查集就是多个并查集, 然后一个set所代表的的关系不仅一种, 所以对每个元素要用多重表示,在这个题目里 就是+i*n来区分每个元素和另一些元素的不同关系 */ #include<iostream> #include<string> #include<algorithm> #include<cstdio> #include<cstring> #include<cmath> using namespace std; const int maxn = 200000+10; int par[maxn]; int dsu(int x){ return x==par[x]?x:(par[x]=dsu(par[x])); } bool same(int x,int y){ return dsu(x)==dsu(y); } int main(){ int n,m; scanf("%d%d",&n,&m); for(int i=0;i<=3*n;i++){ par[i]=i; } int cnt=0; int x,y,ty; while(m--){ scanf("%d%d%d",&ty,&x,&y); if(x<=0||x>n||y<=0||y>n){ cnt++; continue; } if(ty==1){ if(same(x,y+n)||same(y,x+n)){ cnt++; }else{ for(int i=0;i<3;i++){ int fx=dsu(x+i*n); int fy=dsu(y+i*n); par[fx]=fy; } } }else{ if(x==y||same(x,y)||same(y,x+n)){ cnt++; }else{ for(int i=0;i<3;i++){ int fx=dsu(x+i*n); int fy=dsu(y+(i+1)%3*n); par[fx]=fy; } } } } printf("%d\n",cnt); return 0; }