警察局决定从两个帮派青龙帮和白蛇帮开始治理混乱,首先需要确定犯罪分子属于哪个团伙。比如,有两名罪犯,他们是否属于同一帮派?警察必须根据不完整的信息做出判断。假设有 N 个罪犯,编号为 1 到 N,其中至少一人属于青龙帮,至少一人属于白蛇帮,请依次给出 M 个消息,消息类型有两种:D a b,表示 a 和 b 属于不同的帮派。A a b,表示查询 a 和 b 是否属于同一帮派。
第 1 行包含单个整数,即测试用例的个数。每个测试用例都以两个整数 N 和 M 开始;接着是 M 行,每行都包含如上所述的一个信息。
对每个查询操作,都根据之前获得的信息进行判断,答案可能是 In the same gangs、In the different gangs 和 Not sure yet,分别表示在同一帮派中、在不同帮派中和还不确定。
1
5 5
A 1 2
D 1 2
A 1 2
D 2 4
A 1 4
Not sure yet.
In different gangs.
In the same gang.
可以用一个集合表示一个帮派,根据集合号判定是否属于同一帮派。在并查集的基本操作中,Union(x、y) 表示将 x 和 y 合并为同一集合。与并查集的集合合并不同,本问题需要将两者划分为不同的集合。
可以给每个节点 x 都复制一个影子 x+n,将 x、y 划分到不同的集合,只需将 x 和 y 的影子(y+n)合并到一个集合,并将 x 的影子(x+n)和 y 合并为同一集合,执行 Union(x,y+n)、Union(x+n,y),表示将 x 和 y 划分到不同的集合。
因为将 x 和 y 划分到不同集合,与彼此的影子进行了合并,即 x 和 y 的影子(y+n)集合号相同或者 x 的影子(x+n)和 y 集合号相等时,说明 x、y 属于不同的集合;而 x 和 y 集合号相等或者 x 的影子和 y 的影子集合号相等时,说明 x、y 属于同一集合;对于其他情况,不确定是否属于同一集合。
若将高树合并到矮树之下,则合并后的树高增1;若将矮树合并到高树之下,则合并的树高不变。树高越大,查找祖宗时经过的节点越多,效率越低。因此采用启发式合并,将矮树合并到高树之下,若树的高度一样,则合并后树根的高度增1.
将 n 扩大为 2n 个节点,初始化每个节点的集合号都为其自身高度,高度为0。
执行 Union(x,y+n)、Union(x+n,y)、表示 x、y 属于不同的集合。
若 Find(y+n)==Find(x)||Find(x+n)==Find(y),则属于不同的集合;若(Find(x)==Find(y)||Find(x+n)==Find(y+n)),则属于同一集合;否则不确定是否属于同一集合。
根据样例,一共有 5 个节点,将其扩大为 10 个节点。初始化每个节点的集合号为自身,高度 h 为 0。

查询 1 和 2 是否属于同一帮派。Find(1)=1,Find(6)=6,Find(2)=2,Find(7)=7,前两个判定条件都不满足,不确定 1 和 2 是否属于同一帮派。
将 1 和 2 划分到不同的集合。将 1 和 2+5 合并,将 1+5 和 2 合并。

查询 1 和 2 是否属于同一帮派。因为 (Find(2+5)==Find(1)|| Find(1+5)==Find(2)),所以 1 和 2 不属于不一帮派。
将 2 和 4 划分到不同的集合。和一将 2 和 4+5 进行合并,将 2+5 和 4 合并。将高度小的树合并到高度大的树下面,因此将 9 合并 到 2 下面,将 4 合并到 7 下面。

表示查询 1 和 4 是否属于同一帮派。因为 Find(1)==Find(4)|| Find(1+5)==Find(4+5),因此 1 和 4 属于同一帮派。
- package com.platform.modules.alg.alglib.poj1703;
-
- public class Poj1703 {
- public String output = "";
- private final int MAXN = 200010; // 因为有影子数据量*2,不然会越界!!run time error!!!
-
- private int n;
- private int m;
- private int fa[] = new int[MAXN];
- private int h[] = new int[MAXN]; // h 用来区分树的高度,但其不存储树的具体高度
-
-
- void Init() {
- for (int i = 1; i <= 2 * n; i++) {
- fa[i] = i;
- h[i] = 0;
- }
- }
-
- int Find(int x) {
- if (x != fa[x])
- fa[x] = Find(fa[x]);
- return fa[x];
- }
-
- void Union(int x, int y) {
- int a = Find(x);
- int b = Find(y);
- if (a == b) return;
- if (h[a] > h[b]) //启发式合并,把矮树合并到高树之下
- fa[b] = a;
- else {
- fa[a] = b;
- if (h[a] == h[b])//若树的高度一样,合并后树根的树高+1
- h[b]++;
- }
- }
-
- public String cal(String input) {
- String[] line = input.split("\n");
- String[] words = line[0].split(" ");
- n = Integer.parseInt(words[0]);
- m = Integer.parseInt(words[1]);
- Init();
- for (int i = 0; i < m; i++) {
- String[] word = line[1 + i].split(" ");
- char ch[] = new char[2];
- ch[0] = word[0].charAt(0);
- int x = Integer.parseInt(word[1]);
- int y = Integer.parseInt(word[2]);
- if (ch[0] == 'D') {
- Union(x, y + n);
- Union(x + n, y);
- } else {
- if (Find(y + n) == Find(x) || Find(x + n) == Find(y))
- output += "In different gangs.\n";
- else if (Find(x) == Find(y) || Find(x + n) == Find(y + n))
- output += "In the same gang.\n";
- else
- output += "Not sure yet.\n";
- }
- }
- return output;
- }
- }
