审题:
本题需要我们通过解析所有人之间的关系,从而判断出朋友团体的总个数并输出
思路:
方法一:扩展域并查集
由于这里涉及对朋友/敌人等关系集合的频繁操作,所以我们需要使用并查集来操作,但是普通的并查集只有一种集合域,也就是只能维护一种关系。这里有两种关系存在,常规并查集已经无法满足要求,所以我们需要使用扩展域并查集
补充:扩展域并查集
相比于普通并查集来说,这种并查集可以维护更多的关系,具体的实现逻辑如下
1.将fa数组的集合域分为朋友域和敌人域
朋友域为1~n,敌人域为1+n~n+n
2.对于x元素来说,和x在同一个集合的是朋友,和x+n在同一个集合的是敌人
讲解操作过程:
假设现在人数n为3,1和2是敌人,2和3是敌人。
接下来我们按照扩展域并查集的逻辑进行模拟操作
由于1和2是敌人,所以我们将1和2的敌人域连起来,将2和1的敌人域连起来,同理后面2和3也近似操作
按照题意,敌人的敌人就是朋友,所以1和3应该是朋友。而我们看到经过前面的操作,1和3处于同一个集合,满足题意,方法成立
解题:
#include<iostream> using namespace std; const int N = 1010; int n, m; int fa[N * 2]; int find(int x) {return fa[x] == x ? x : fa[x] = find(fa[x]); } void un(int x, int y)//让朋友域当根节点 {fa[find(x)] = find(y); }int main() {cin >> n >> m;//初始化扩展并查集for (int i = 1; i <= 2 * n; i++){fa[i] = i;}//建立关系for (int i = 1; i <= m; i++){char x;int y, z;cin >> x >> y >> z;if (x == 'E')//敌人关系{un(z + n, y);un(y + n, z);}else//朋友关系{un(y, z);}}//查询团伙数int ret = 0;for (int i = 1; i <= n; i++){if (fa[i] == i) ret++;}cout << ret << endl;return 0; }
注意:
1.题目中只说了两个条件:一个是敌人的敌人是朋友,另一个是朋友的朋友是朋友。
但是没有说朋友的敌人是不是敌人,敌人的朋友是不是敌人,所以我们不需要进行特别的其他操作
2.由于实际上存在的人数是n,敌人域是我们自己构建的,所以我们最后统计团伙的时候不能统计到敌人域,只需要统计前n个即可
3.由于我们统计的是朋友域,所以我们的根节点一定要是朋友域的节点,否则就无法统计到了,在传参给un函数的时候要注意顺序
P1892 [BalticOI 2003] 团伙 - 洛谷