算法学习专栏——并查集
一、合并集合(简单+)
一共有 n 个数,编号是 1∼n,最开始每个数各自在一个集合中。
现在要进行 m 个操作,操作共有两种:
M a b,将编号为 a 和 b 的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;Q a b,询问编号为 a 和 b 的两个数是否在同一个集合中;
输入格式
第一行输入整数 n 和 m。
接下来 m 行,每行包含一个操作指令,指令为 M a b 或 Q a b 中的一种。
输出格式
对于每个询问指令 Q a b,都要输出一个结果,如果 a 和 b 在同一集合内,则输出 Yes,否则输出 No。
每个结果占一行。
数据范围
1≤n,m≤10^5^
输入样例:
1 | |
输出样例:
1 | |
思路
直播(录屏)
代码
答案(请自己先思考一下再参考)
#include < iostream>
#include < algorithm>
using namespace std;
const int N = 1e5 + 10;
int p[N];
int n, m;
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) p[i] = i;
for (int i = 0; i < m; i++) {
char op[2];
int x, y;
cin >> op >> x >> y;
if (op[0] == 'M') {
p[find(x)] = find(y);
} else {
if (find(x) == find(y)) cout << "Yes\n";
else cout << "No\n";
}
}
return 0;
}
二、连通块中点的数量(简单++)
给定一个包含 n 个点(编号为 1∼n)的无向图,初始时图中没有边。
现在要进行 m 个操作,操作共有三种:
C a b,在点 a 和点 b 之间连一条边,a 和 b 可能相等;Q1 a b,询问点 a 和点 b 是否在同一个连通块中,a 和 b 可能相等;Q2 a,询问点 a 所在连通块中点的数量;
输入格式
第一行输入整数 n 和 m。
接下来 m 行,每行包含一个操作指令,指令为 C a b,Q1 a b 或 Q2 a 中的一种。
输出格式
对于每个询问指令 Q1 a b,如果 a 和 b 在同一个连通块中,则输出 Yes,否则输出 No。
对于每个询问指令 Q2 a,输出一个整数表示点 a 所在连通块中点的数量
每个结果占一行。
数据范围
1≤n,m≤10^5^
输入样例:
1 | |
输出样例:
1 | |
思路
直播(录屏)
代码
答案(请自己先思考一下再参考)
#include < iostream>
#include < algorithm>
using namespace std;
const int N = 1e5 + 10;
int p[N], s[N];
int n, m;
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
char op[3];
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
p[i] = i;
s[i] = 1;
}
for (int i = 0; i < m; i++) {
cin >> op;
if (op[0] == 'C') {
int x, y;
cin >> x >> y;
if (find(x) == find(y)) continue;
s[find(x)] += s[find(y)];
p[find(y)] = find(x);
} else if (op[1] == '1') {
int x, y;
cin >> x >> y;
if (find(x) == find(y)) cout << "Yes\n";
else cout << "No\n";
} else {
int x;
cin >> x;
cout << s[find(x)] << '\n';
}
}
return 0;
}
三、食物链(中等+)
动物王国中有三类动物 A,B,C 这三类动物的食物链构成了有趣的环形。
A 吃 B,B 吃 C,C 吃 A。
现有 N 个动物,以 1∼N编号。
每个动物都是 A,B,C 中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这 N 个动物所构成的食物链关系进行描述:
第一种说法是 1 X Y,表示 X 和 Y 是同类。
第二种说法是 2 X Y,表示 X 吃 Y。
此人对 N 个动物,用上述两种说法,一句接一句地说出 K 句话,这 K 句话有的是真的,有的是假的。
当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
- 当前的话与前面的某些真的话冲突,就是假话;
- 当前的话中 X 或 Y 比 N 大,就是假话;
- 当前的话表示 X 吃 X,就是假话。
你的任务是根据给定的 N 和 K 句话,输出假话的总数。
输入格式
第一行是两个整数 N 和 K,以一个空格分隔。
以下 K 行每行是三个正整数 D,X,Y两数之间用一个空格隔开,其中 D 表示说法的种类。
若 D=1=1,则表示 X 和 Y 是同类。
若 D=2=2,则表示 X 吃 Y。
输出格式
只有一个整数,表示假话的数目。
数据范围
1≤N≤50000
0≤K≤1000000
输入样例:
1 | |
输出样例:
1 | |
思路
直播(录屏)
有疑点抓紧时间问本人。
代码
答案(请自己先思考一下再参考)
#include < iostream>
#include < algorithm>
using namespace std;
const int N = 5e5 + 10;
int p[N], d[N];
int n, k, ans;
int find(int x){
if (p[x] != x) {
int t = find(p[x]);
d[x] += d[p[x]];
p[x] = t;
}
return p[x];
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin >> n >> k;
for (int i = 1; i <= n;i++) p[i] = i;
for (int i = 0; i < k; i++) {
int a,b,c;
cin >> a >> b >> c;
if (b > n || c > n) ans++;
else {
int pb = find(b),pc = find(c);
if (a == 1) {
if (pb == pc && (d[b] - d[c]) % 3) ans++;
else if (pb != pc) {
p[pb] = pc;
d[pb] = d[c] - d[b];
}
} else {
if (pb == pc && (d[b] - d[c] - 1) % 3) ans++;
else if (pb != pc) {
p[pb] = pc;
d[pb] = d[c] - d[b] + 1;
}
}
}
}
cout << ans;
return 0;
}