一、带边权的并查集
另外修改一下路径压缩的代码,让结点加上父结点距祖宗结点的距离即可
//路径压缩
int find(int x){
if(x != p[x]){
int t = find(p[x]);
d[x] += d[p[x]];
p[x] = t;
}
return p[x];
}
完整代码如下
#include<iostream>
#include<cstring>
using namespace std;
const int N = 50010;
int n, m;
int p[N], d[N];
int find(int x){
if(x != p[x]){
int t = find(p[x]);
d[x] += d[p[x]];
p[x] = t;
}
return p[x];
}
int main(){
cin >> n >> m;
for(int i = 1; i <= n; ++ i) p[i] = i;
int res = 0;
while(m --){
int t, x, y;
cin >> t >> x >> y;
if(x > n || y > n) res ++;
else{
int px = find(x), py = find(y);
if(t == 1){
if(px == py && (d[x] - d[y]) % 3) res ++;
else if(px != py){
p[px] = py;
d[px] = d[y] - d[x];
}
}else{
if(px == py && (d[x] - 1 - d[y]) % 3) res ++;
else if(px != py){
p[px] = py;
d[px] = d[y] + 1 - d[x];
}
}
}
}
cout << res << endl;
return 0;
}
二、带扩展域的并查集
带扩展域的代码比较好想,也好写,具体代码如下
#include<iostream>
#include<cstring>
using namespace std;
const int N = 150010;
int n, m;
int p[N];
int find(int x){
if(x != p[x]) p[x] = find(p[x]);
return p[x];
}
void merge(int x, int y){
p[find(x)] = find(y);
}
int main(){
cin >> n >> m;
for(int i = 1; i <= 3 * n; ++ i) p[i] = i;
int res = 0;
while(m --){
int t, x, y;
cin >> t >> x >> y;
if(x > n || y > n){
res ++;
continue;
}
if(t == 1){
if(find(x) == find(y + n) || find(x) == find(y + n + n)) res ++;
else{
merge(x, y);
merge(x + n, y + n);
merge(x + n + n, y + n + n);
}
}else{
if(find(x) == find(y) || find(x) == find(y + n)) res ++;
else{
merge(x, y + n + n);
merge(x + n, y);
merge(x + n + n, y + n);
}
}
}
cout << res << endl;
return 0;
}
/*
+ 0是同类域
+ n是捕食域
+ 2 * n是天敌域
*/