目录
1.概论
2.dfs + 根节点模拟
3.题目含义
4.具体操作
5.代码
6.题目
1.概论
并查集是一种树形数据结构,主要实现并和查的功能。即合并两个集合或者查找某个元素在哪个集合中。
2.dfs + 根节点模拟
dfs可以帮助我们遍历一棵树,而我们可以通过储存根节点的方式,在遍历过程中给该树中所有的子节点分发该子节点的最大上司(根节点)。从而在访问到每个节点时,就可以通过该节点储存的最大上司,知道该节点在哪个树中。
3.题目含义
题目要求在G中,如果两点之间有路径相连则F中也一定要有,如果G中两点没有路径,但是F中有,则要删除F中的一条边。如果在 G 中两点有路径但是 F 中没有则需要添加一条边。
4.具体操作
根据树的性质,如果两个点在一个树里,则两个点之间一定存在一个连通路径可以抵达。所以我们将问题转换为判断每个点是否与树根连通,如果同样的一个点在F和G中连着不同的树根就是我们要处理的是另外两种情况。
第一种是在 G 中两点没有连通,但是在 F 中有连通,对应了代码是:
if (gc[now] != c)
第二种是在 G 中两点连通,但是在 F 中两点没有连通,对应了代码是:
if (gcol[i] != i + 1) ans++;
5.代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
void gfs(int u, int c, vector<vector<int>> &gl, vector<int> &gc) {
gc[u] = c;
for (int now : gl[u]) {
if (gc[now] == 0) {
gfs(now, c, gl, gc);
}
}
}
int ffs(int u, int c, vector<vector<int>> &fl, vector<int> &gc, vector<int> &fc) {
int res = 0;
fc[u] = c;
for (int now : fl[u]) {
if (fc[now] == 0) {
if (gc[now] != c) {
res++;//在 F 中该边连接了和 G 不同的根节点,则删除该边。
}
else res += ffs(now, c, fl, gc, fc);
}
}
return res;
}
void com_list(int m, vector<vector<int>> &sl) {
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
sl[--u].emplace_back(--v);
sl[v].emplace_back(u);
}
}
void solve() {
int n, m1, m2;
cin >> n >> m1 >> m2;
vector<vector<int>> fline(n);
vector<vector<int>> gline(n);
com_list(m1, fline);
com_list(m2, gline);
vector<int> fcol(n);
vector<int> gcol(n);
int ans = 0;
for (int i = 0; i < n; i++) {
if (gcol[i] == 0) {
gfs(i, i + 1, gline, gcol);
}
if (fcol[i] == 0) {//表示i当前是F中一个树的根节点
ans += ffs(i, gcol[i], fline, gcol, fcol);
if (gcol[i] != i + 1) ans++;//G 中该点对应的树根与F中的点对应的树根不一样
}
}
cout << ans << endl;
}
signed main() {
int t;
cin >> t;
while (t--) {
solve();
}
}