本专题用来记录并查集相关的题目。
并查集模板:
//初始化
for (int i = 1; i <= n; ++i) { //n为结点数目
p[i] = i;
}
//查找
find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
//合并
int pa = find(a);
int pb = find(b);
if (pa != pb) {
p[pa] = pb;
}
题目1:1250格子游戏
C++代码如下,
#include
#include
#include
using namespace std;
const int N = 210;
int n, m;
int p[N * N];
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main() {
cin >> n >> m;
for (int i = 0; i < n * n; ++i) p[i] = i;
for (int i = 1; i <= m; ++i) {
int x, y;
char op;
cin >> x >> y >> op;
int a, b;
if (op == 'D') {
a = (x - 1) * n + y - 1;
b = x * n + y - 1;
} else {
a = (x - 1) * n + y - 1;
b = (x - 1) * n + y;
}
int pa = find(a);
int pb = find(b);
if (pa == pb) {
cout << i << endl;
return 0;
} else {
p[pa] = pb;
}
}
cout << "draw" << endl;
return 0;
}
题目2:1252搭配购买
C++代码如下,
#include
#include
#include
#include
using namespace std;
const int N = 10010;
int n, m, W;
int c[N], w[N];
int p[N];
int sc[N]; //每个连通块的总价格,只针对根结点
int sw[N]; //每个连通块的总价值
int f[N];
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main() {
cin >> n >> m >> W;
for (int i = 1; i <= n; ++i) {
cin >> c[i] >> w[i];
}
for (int i = 1; i <= n; ++i) {
p[i] = i;
sc[i] = c[i];
sw[i] = w[i];
}
for (int i = 1; i <= m; ++i) {
int a, b;
cin >> a >> b;
int pa = find(a);
int pb = find(b);
if (pa != pb) {
p[pa] = pb;
sc[pb] += sc[pa];
sw[pb] += sw[pa];
}
}
//01背包问题
int idx = 0;
for (int i = 1; i <= n; ++i) {
if (i == p[i]) {
sc[idx] = sc[i];
sw[idx] = sw[i];
idx++;
}
}
for (int i = 0; i < idx; ++i) {
for (int j = W; j >= sc[i]; --j) {
f[j] = max(f[j], f[j - sc[i]] + sw[i]);
}
}
cout << f[W] << endl;
return 0;
}
题目3:237程序自动分析
C++代码如下,
#include
#include
#include
#include
using namespace std;
typedef pair<int, int> PII;
const int N = 200010, M = 1e9 + 10;
int p[N];
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main() {
int T;
cin >> T;
while (T--) {
memset(p, 0, sizeof p);
unordered_map<int, int> map_b_s;
int n;
cin >> n;
vector<PII> equals, not_equals;
int idx = 0;
for (int i = 0; i < n; ++i) {
int a, b, c;
cin >> a >> b >> c;
if (c == 0) {
not_equals.emplace_back(a,b);
} else {
equals.emplace_back(a,b);
}
if (map_b_s.count(a) == 0) {
map_b_s[a] = idx++;
}
if (map_b_s.count(b) == 0) {
map_b_s[b] = idx++;
}
}
for (int i = 0; i < idx; ++i) p[i] = i;
for (auto [a, b] : equals) {
a = map_b_s[a];
b = map_b_s[b];
int pa = find(a);
int pb = find(b);
if (pa != pb) {
p[pa] = pb;
}
}
bool flag = true;
for (auto [a, b] : not_equals) {
a = map_b_s[a];
b = map_b_s[b];
int pa = find(a);
int pb = find(b);
if (pa == pb) {
flag = false;
cout << "NO" << endl;
break;
}
}
if (flag) {
cout << "YES" << endl;
}
}
return 0;
}
题目4:239奇偶游戏
C++代码如下,
//带边权写法
#include
#include
#include
#include
using namespace std;
const int N = 20010;
int n, m;
int p[N], d[N];
unordered_map<int, int> S;
int get(int x) {
if (S.count(x) == 0) S[x] = ++n;
return S[x];
}
int find(int x) {
if (p[x] != x) {
int root = find(p[x]);
d[x] += d[p[x]];
p[x] = root;
}
return p[x];
}
int main() {
cin >> n >> m;
n = 0;
for (int i = 0; i < N; ++i) p[i] = i;
int res = m;
for (int i = 1; i <= m; ++i) {
int a, b;
string type;
cin >> a >> b >> type;
a = get(a - 1), b = get(b);
int t = 0;
if (type == "odd") t = 1;
int pa = find(a), pb = find(b);
if (pa == pb) {
if (((d[a] + d[b]) % 2 + 2) % 2 != t) {
res = i - 1;
break;
}
} else {
p[pa] = pb;
d[pa] = d[a] ^ d[b] ^ t;
}
}
cout << res << endl;
return 0;
}
题目5:238银河英雄传说
C++代码如下,
#include
#include
#include
using namespace std;
const int N = 30010;
int n;
int p[N];
int d[N]; //到根结点的距离
int cnt[N];
int find(int x) {
if (p[x] != x) {
int root = find(p[x]);
d[x] += d[p[x]];
p[x] = root;
}
return p[x];
}
int main() {
cin >> n;
for (int i = 1; i < N; ++i) {
p[i] = i;
cnt[i] = 1;
d[i] = 0;
}
for (int i = 0; i < n; ++i) {
char op;
int a, b;
cin >> op >> a >> b;
int pa = find(a), pb = find(b);
if (op == 'M') {
if (pa != pb) {
d[pa] = cnt[pb];
cnt[pb] += cnt[pa];
p[pa] = pb;
}
} else {
if (pa != pb) puts("-1");
else {
cout << max(abs(d[a] - d[b]) - 1, 0) << endl;
}
}
}
return 0;
}