并查集
维护一下不相交的集合,支持两种操作:合并两个集合,查询一股元素所处的集合
路径压缩:
沿着树根找到
a
a
a 元素所在集合的代表
b
b
b 后,对这一条路径上的所有元素
x
x
x,
f
a
[
x
]
=
b
fa[x] = b
fa[x]=b
按秩合并:
深度较小的合并到深度较大的集合上。每个集合维护一个
r
a
n
k
rank
rank 值,将
r
a
n
k
rank
rank 小的合并到
r
a
n
k
rank
rank 大的集合,如果
r
a
n
k
rank
rank 相等,则
r
a
n
k
=
r
a
n
k
+
1
rank = rank + 1
rank=rank+1
struct DSU{
vector<int> fa;
vector<int> rank;
DSU(int n) : fa(n), rank(n){
for(int i = 1; i <= n; i++)
fa[i] = i;
}
int find(int x){
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void merge(int x, int y){
int fx = find(x);
int fy = find(y);
if(rank[fx] < rank[fy])
fa[fx] = fy;
else{
fa[fy] = fx;
if(rank[fx] == rank[fy])
rank[fx]++;
}
}
};
P1525 [NOIP2010 提高组] 关押罪犯
题意:
有 n n n 名罪犯和 2 2 2 座监狱。如果两名罪犯之间的仇恨值为 c c c ,且在同一监狱,则造成影响为 c c c 的冲突事件。
询问,如何分配罪犯,使最大的冲突值最小
解析:
按 c c c 降序排序,依次处理每组罪犯 ( a , b ) (a,b) (a,b)
- 如果 a , b a,b a,b 已经在一个监狱,答案为 c c c
- 将 a , b a,b a,b 分配到两个监狱
对于需要分配的情况,每组都有两种分配的方法,但注意到 b b b 的两个敌人在同一监狱,记录每个人的敌人。分配监狱时,把 a a a 分配到 b b b 的敌人所在监狱, b b b 同理
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
struct peo{
int a, b, c;
bool operator < (const peo &x) const{
return c > x.c;
}
}a[maxn];
int f[maxn];
int find(int x){
return x == f[x] ? x : f[x] = find(f[x]);
}
void merge(int x, int y){
int fx = find(x);
int fy = find(y);
if(fx != fy)
f[fx] = fy;
return;
}
int b[maxn];
int n, m;
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++)
f[i] = i;
for(int i = 1; i <= m; i++)
cin >> a[i].a >> a[i].b >> a[i].c;
sort(a+1, a+1+m);
for(int i = 1; i <= m; i++){
int fx = find(a[i].a);
int fy = find(a[i].b);
if(fx == fy){
cout << a[i].c << endl;
return 0;
}
else{
if(!b[a[i].a])
b[a[i].a] = a[i].b;
else
merge(a[i].b, b[a[i].a]);
if(!b[a[i].b])
b[a[i].b] = a[i].a;
else
merge(b[a[i].b], a[i].a);
}
}
cout << 0 << endl;
return 0;
}
P2024 [NOI2001] 食物链
题意:
三类动物 A , B , C A, B, C A,B,C , A A A 吃 B B B , B B B 吃 C C C , C C C 吃 A A A。
n n n 个动物,和 k k k 句话。每句话有两种描述: X X X 和 Y Y Y 是同类; X X X 吃 Y Y Y。
如果某一句话与前边真话冲突,则是假话,询问假话数目
解析:
用 3 3 3 倍并查集存关系,一倍存本身,二倍存食物,三倍存天敌
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 2e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;
struct DSU{
vector<int> fa;
vector<int> rank;
DSU(int n) : fa(n), rank(n){
for(int i = 1; i <= n; i++)
fa[i] = i;
}
int find(int x){
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void merge(int x, int y){
int fx = find(x);
int fy = find(y);
if(rank[fx] < rank[fy])
fa[fx] = fy;
else{
fa[fy] = fx;
if(rank[fx] == rank[fy])
rank[fx]++;
}
}
};
//1倍本身 2倍食物 3倍天敌
int n, k, ans;
int main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> k;
DSU dsu(3*n);
int op, x, y;
for(int i = 1; i <= k; i++){
cin >> op >> x >> y;
if(x > n || y > n){
ans++;
continue;
}
if(op == 1){
if(dsu.find(x)==dsu.find(y+n) || dsu.find(x)==dsu.find(y+2*n)){
ans++;
continue;
}
dsu.merge(x, y);
dsu.merge(x+n, y+n);
dsu.merge(x+2*n, y+2*n);
}
else{
if(x == y){
ans++;
continue;
}
if(dsu.find(x)==dsu.find(y) || dsu.find(x+2*n)==dsu.find(y)){
ans++;
continue;
}
dsu.merge(x, y+2*n);
dsu.merge(x+n, y);
dsu.merge(x+2*n, y+n);
}
}
cout << ans << endl;
return 0;
}
P1682 过家家
题意:
n n n 个男生, n n n 个女生。有些女生之间是朋友关系,有些男生和女生之间是不吵架关系,朋友关系可以传递。女生可以和不吵架的男生玩一轮游戏,也可以和朋友女生不吵架的男生完。
每个女生最多可以和 k k k 个吵架的男生玩游戏
询问做多玩几轮游戏
解析:
女生朋友关系用并查集维护,记录每个并查集连通块对应的吵架的男生数目。
连通块对应男生数目最小值为不使用“强制”轮数,加上k即最多轮数,最后和 n n n 取min
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define mkp(a, b) make_pair((a), (b))
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;
struct DSU{
vector<int> fa;
vector<int> rank;
DSU(int n) : fa(n), rank(n){
for(int i = 1; i <= n; i++)
fa[i] = i;
}
int find(int x){
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void merge(int x, int y){
int fx = find(x);
int fy = find(y);
if(rank[fx] < rank[fy]){
fa[fx] = fy;
}
else{
fa[fy] = fx;
if(rank[fx] == rank[fy])
rank[fx]++;
}
}
};
set<int> cnt[maxn];
vector<pii> boygirl;
int n, m, k, f;
int main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> m >> k >> f;
DSU dsu(2*n);
for(int i = 1; i <= m; i++){
int a, b;
cin >> a >> b;
boygirl.push_back(mkp(a, b));
}
for(int i = 1; i <= f; i++){
int a, b;
cin >> a >> b;
dsu.merge(a, b);
}
for(int i = 0; i < boygirl.size(); i++){
pii s = boygirl[i];
int girl = s.fi;
int boy = s.se;
cnt[dsu.find(girl)].insert(boy);
}
int res = INF;
for(int i = 1; i <= n; i++){
int siz = cnt[dsu.find(i)].size();
res = min(res, siz);
}
res = min(res+k, n);
cout << res << endl;
return 0;
}