查找+路径压缩
int find(int x){
if(pre[x] == x) return x;
return pre[x] = find(pre[x]);
}
合并
void Union(int x, int y){
int fx = find(x), fy = find(y);
if(fx != fy) pre[fx] = fy;
}
思路:
#include
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define PII pair<int, int>
#define pi acos(-1.0)
const int INF = 0x7fffffff;
const int N = 1e5 + 5;
const db eps = 1e-10;
using namespace std;
int n, m, pre[N];
int find(int x){
if(pre[x] == x) return x;
return pre[x] = find(pre[x]);
}
void join(int x,int y){
int fx = find(x), fy = find(y);
if(fx != fy) pre[fx] = fy;
}
int main(){
cin >> n >> m;
rep(i, 1, n) pre[i] = i;
rep(i, 1, m){
int op, x, y; scanf("%d%d%d", &op, &x, &y);
if(op == 1) join(x, y);
else puts(find(x) == find(y) ? "Y" : "N");
}
}
#include
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define PII pair<int, int>
#define pi acos(-1.0)
const int INF = 0x7fffffff;
const int N = 1e3 + 5, M = 1e5 + 5;
const db eps = 1e-10;
using namespace std;
int m, n, pre[N], now = 0, cnt = 0;
struct AC{
int x, y, t;
}a[M];
bool cmp(AC a, AC b){
return a.t < b.t;
}
int find(int x){
if(pre[x] == x) return x;
return pre[x] = find(pre[x]);
}
void join(int x, int y){
int fx = find(x), fy = find(y);
if(fy != fx) pre[fx] = fy;
}
int main(){
cin >> n >> m;
rep(i, 1, m) scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].t);
sort(a + 1, a + 1 + m, cmp);
rep(i, 1, n) pre[i] = i;
rep(i, 1, m){
int nx = a[i].x, ny = a[i].y;
if(find(nx) != find(ny)){ //只统计可连接不同集合内的
join(nx, ny);
now = a[i].t;
cnt++;
}
if(cnt == n - 1){ //n个节点只需要 n - 1 条连接两个不同集合内的边即可全部连接
cout << now << endl;
return 0;
}
}
if(cnt < n - 1) cout << -1 << endl;
}
//并查集 && 逆推思想
#include
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define PII pair<int, int>
#define pi acos(-1.0)
const int INF = 0x7fffffff;
const int N = 4e5 + 5;
const db eps = 1e-10;
using namespace std;
int m, n, k, a[N], pre[N], ans[N];
bool vis[N];
vector<int> g[N];
int find(int x){
if(pre[x] == x) return x;
return pre[x] = find(pre[x]);
}
int main(){
cin >> n >> m;
rep(i, 1, m){
int x, y; scanf("%d%d", &x, &y);
g[x].push_back(y), g[y].push_back(x);
}
cin >> k;
rep(i, 0, n - 1) vis[i] = 1, pre[i] = i;
rep(i, 1, k){
scanf("%d", &a[i]);
vis[a[i]] = 0;
}
ans[k + 1] = n - k;
rep(i, 0, n - 1){ //初始化
if(!vis[i]) continue;
for(auto nxt : g[i]){
if(!vis[nxt]) continue;
int fx = find(nxt), fy = find(i); //Union
if(fx != fy){
pre[fx] = fy;
ans[k + 1]--;
}
}
}
per(i, k, 1){ //逆推
int u = a[i];
vis[u] = 1, ans[i] = ans[i + 1] + 1; //加入一个a[i]的连通块
for(auto nxt : g[u]){
if(!vis[nxt]) continue;
int fx = find(nxt), fy = find(u);
if(fx != fy){
pre[fx] = fy;
ans[i]--;
}
}
}
rep(i, 1, k + 1) cout << ans[i] << endl;
}
v a l [ i ] val[i] val[i]记录节点 i i i与父节点的关系
查找+路径压缩
int find(int x){
if(x == pre[x]) return x;
int root = find(pre[x]); //一直找到根节点
...//压缩时要更新权值,此时val[pre[x]]在上一行已更新完
return pre[x] = root;
}
合并
void Union(int x, int y){
int fx = find(x), fy = find(y);
if(fx != fy){
pre[fx] = fy;
...//更新权值
}
}
#include
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define PII pair<int, int>
#define pi acos(-1.0)
const int INF = 0x7fffffff;
const int N = 3e4 + 5;
const db eps = 1e-10;
using namespace std;
int t, n, a[N], pre[N], val[N], sz[N], x, y; //sz数组记录所在列的大小, val[i]数组记录 i 到本列根节点的距离
char op;
int find(int x){
if(x == pre[x]) return x;
int fx = find(pre[x]);
val[x] = val[pre[x]] + val[x]; //val[x] 表示 x->pre[x], val[fx] 表示 pre[x]->fx. 上一行函数已更新 val[pre[x]]
sz[x] = sz[fx];
return pre[x] = fx;
}
void Union(int x, int y){
int fx = find(x), fy = find(y);
pre[fx] = fy;
val[fx] += sz[fy];
sz[fy] = sz[fx] = sz[fx] + sz[fy];
}
int main(){
cin >> t;
rep(i, 1, N) pre[i] = i, val[i] = 0, sz[i] = 1;
rep(i, 1, t){
cin >> op >> x >> y;
if(op == 'M') Union(x, y);
else{
if(find(x) == find(y)) cout << abs(val[x] - val[y]) - 1 << endl;
else cout << -1 << endl;
}
rep(i, 1, 4) cout << i << " " << pre[i] << " " << val[i] << endl;
}
}
种类并查集一般可以用扩展域并查集,也就是增加维度,每个元素的每个维度都是一个节点,判断两个节点是否在同一个集合。
思路:
//扩展域并查集
#include
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
const int INF = 0x7fffffff;
const int N = 3e5 + 5;
using namespace std;
int k, n, pre[N], op[N], x[N], y[N], res = 0;
int find(int x){
if(pre[x] == x) return x;
return pre[x] = find(pre[x]);
}
void Union(int x, int y){
int fx = find(x), fy = find(y);
if(fx != fy) pre[fx] = fy;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin >> n >> k;
rep(i, 1, 3 * n) pre[i] = i;
rep(i, 1, k){
cin >> op[i] >> x[i] >> y[i];
if(x[i] > n || y[i] > n || (op[i] == 2 && x[i] == y[i])){
res++, cout << i << endl;
continue;
}
int xself = x[i], xenemy = x[i] + n, xeat = x[i] + n + n;
int yself = y[i], yenemy = y[i] + n, yeat = y[i] + n + n;
if(op[i] == 1){
if(find(xself) == find(yeat) || find(xself) == find(yenemy)) res++, cout << i << endl;
else Union(xself, yself), Union(xenemy, yenemy), Union(xeat, yeat);
}
else{
if(find(xself) == find(yself) || find(xself) == find(yeat)) res++, cout << i << endl;
else Union(xself, yenemy), Union(xenemy, yeat), Union(xeat, yself);
}
}
cout << res << endl;
}
当然用带权并查集也能做
//带权并查集
#include
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
const int INF = 0x7fffffff;
const int N = 1e5 + 5;
using namespace std;
int k, n, pre[N], op[N], x[N], y[N], res = 0, val[N];
int find(int x){
if(pre[x] == x) return x;
int root = find(pre[x]);
(val[x] += val[pre[x]]) %= 3; //更新权值
return pre[x] = root;
}
int main(){
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> k;
rep(i, 1, n) pre[i] = i, val[i] = 0;
rep(i, 1, k){
cin >> op[i] >> x[i] >> y[i];
if(x[i] > n || y[i] > n || (op[i] == 2 && x[i] == y[i])){
res++;
continue;
}
int fx = find(x[i]), fy = find(y[i]);
if(fx == fy){
if(op[i] == 1 && val[x[i]] != val[y[i]]) res++;
if(op[i] == 2 && val[x[i]] != (val[y[i]] + 1) % 3) res++;
}
else{
pre[fx] = fy;
val[fx] = (val[y[i]] + (op[i] == 1 ? 0 : 1) - val[x[i]] + 3) % 3;
}
}
cout << res << endl;
}
//带权并查集
#include
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
const int INF = 0x7fffffff;
const int N = 1e5 + 5;
using namespace std;
int k, n, pre[N], op[N], x[N], y[N], res = 0, val[N];
int find(int x){
if(pre[x] == x) return x;
int root = find(pre[x]);
(val[x] += val[pre[x]]) %= 3; //更新权值
return pre[x] = root;
}
void Union(int x, int y, int op){
int fx = find(x), fy = find(y);
if(fx != fy){
pre[fx] = fy;
val[fx] = (val[y] + (op == 1 ? 0 : 1) - val[x] + 3) % 3;
}
}
int main(){
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> k;
rep(i, 1, n) pre[i] = i, val[i] = 0;
rep(i, 1, k){
cin >> op[i] >> x[i] >> y[i];
if(x[i] > n || y[i] > n || (op[i] == 2 && x[i] == y[i])){
res++;
continue;
}
if(op[i] == 1){
if(find(x[i]) == find(y[i]) && val[x[i]] != val[y[i]]) res++;
else Union(x[i], y[i], op[i]);
}
else{
if(find(x[i]) == find(y[i]) && val[x[i]] != (val[y[i]] + 1) % 3) res++;
else Union(x[i], y[i], op[i]);
}
}
cout << res << endl;
}