并查集入门题
POJ 1611
题意:给你各个集合包含的元素,求0所在的集合总共有多少个不相同的元素。
题解:维护一个size即可,注意size是在什么时候进行相加的。
//#include <bits/stdc++.h> #include <cstdio> using namespace std; int fa[30005], size[30005]; int find(int x) { if(fa[x]==x) return x; return fa[x]=find(fa[x]); } int main() { int n, m; while(~scanf("%d%d", &n, &m)) { if(n==0 && m==0) break; for(int i=0; i<n; i++) fa[i]=i, size[i]=1; for(int i=0; i<m; i++) { int cnt, x, y; scanf("%d", &cnt); if(cnt) scanf("%d", &x); for(int i=1; i<cnt; i++) { scanf("%d", &y); int px=find(x), py=find(y); if(px!=py){ size[px]+=size[py]; fa[py]=px; } } } int p=find(0); printf("%d\n", size[p]); } return 0; }
POJ 1988 cube stacking
题意: 有N(N<=30,000)堆方块,开始每堆都是一个方块,方块编号1 – N. 有两种操作:
• M x y : 表示把方块x所在的堆,拿起来叠放到y所在的堆上。
• C x : 问方块x下面有多少个方块。
• 操作最多有 P (P<=100,000)次。对每次C操作,输出结果
题解:形成一种树形结构,直接用带权并查集维护,size,under 即可。注意路径压缩的过程中别写错了。
//#include <bits/stdc++.h> #include <cstdio> using namespace std; const int maxn=30005; int fa[maxn], under[maxn], size[maxn]; int find(int x) { if(fa[x]==x) return x; int t=find(fa[x]); under[x]+=under[fa[x]]; // 注意是加上under[fa[x]]而不是under[t] return fa[x]=t; } void merge(int x, int y) { int px=find(x), py=find(y); if(px!=py){ under[py]=size[px]; //注意在合并之前,把所有要维护的都维护掉 size[px]+=size[py]; fa[py]=px; } } void init(int n) { for(int i=1; i<=maxn; i++) { size[i]=1; under[i]=0; fa[i]=i; } } int main() { int n; scanf("%d", &n); init(n); for(int i=0; i<n; i++) { char cmd[10]; int x, y; scanf("%s", cmd); if(cmd[0]=='M'){ scanf("%d%d", &x, &y); merge(y, x); } else{ scanf("%d", &x); find(x); //注意要先路径压缩后在直接输出 printf("%d\n", under[x]); } } return 0; }
BZOJ 1202 [HNOI2005] 狡猾的商人
题意:给你多个区间u到v的和为w,问是否会有冲突,每个位置的值可以是负数。
题解:判断时候只有知道了x-y,x-z,才能判断y-z是否冲突。故使用并查集维护区间和,根节点为起点,在同一个并查集内必然可以判断是否冲突。
#include <bits/stdc++.h> int val[105], fa[105], ok; int find(int x) { if(fa[x]==x) return x; int t=find(fa[x]); val[x]+=val[fa[x]]; return fa[x]=t; } void merge(int u, int v, int w) { int pu=find(u), pv=find(v); if(pu!=pv){ val[pv]=-val[v]-w+val[u]; fa[pv]=pu; } else{ if(val[v]-val[u]!=-w) ok=0; } } int main() { int T; for(scanf("%d", &T); T--; ) { int n, m; scanf("%d%d", &n, &m); for(int i=0; i<=n; i++) val[i]=0, fa[i]=i; ok=1; for(int i=0; i<m; i++) { int u, v, w; scanf("%d%d%d", &u, &v, &w); merge(u-1, v, w); //起点往左边移动一位方便计算 } printf("%s\n", ok? "true":"false"); } return 0; }