Advanced Data Structures :: Disjoint Set
Description
某个市有两个帮派,且这个市中的所有人,有且仅有在一个帮派中。
告诉你一些信息,即哪两个人不在一个帮派中。
且多次询问两个人,是否在同一个帮派中,或者关系暂时不确定。
对于每个询问,输出询问结果。
Type
Advanced Data Structures :: Disjoint Set
Analysis
利用并查集来解题。
但是并查集通常用来表示,有着相同地位或身份的人,
因为这样的关系有传递性,如你是我的战友,他是你的战友,则我和他也是战友。
而敌对关系没有传递性(你是我的敌人,他是你的敌人,而他不一定是我的敌人),
因此无法用一般的并查集表示。
但是,这样的敌对关系,我们也可以用并查集来表示。
就是将并查集中的边,加权(也可以说是,对每个点增加一个偏移量)。
权值(或者偏移量)用来表示,该节点与父节点的距离
(例如,敌对关系时,距离为1,战友关系则为0),
在同一个并查集森林里,两点之间的距离之和对状态数(上述例子为2)取模,则可以得到两点间的关系。
加权之后,就可以解决这道问题了,并且路径压缩的优化依然可以在加权并查集中使用。
注意当市里只有两个人(不要吐槽)时,这两个人必然是不同团伙的。
Solution
// POJ 1703
// Find them, Catch them
// by A Code Rabbit
#include <cstdio>
#include <cstring>
const int MAXN = 100002;
const int MOD = 2;
struct DisjointSet {
int p[MAXN];
int w[MAXN];
void Init(int);
void Make(int x) { p[x] = x; }
int Find(int x) {
if (p[x] == x) return x;
int res = Find(p[x]);
w[x] = (w[x] + w[p[x]]) % MOD;
return p[x] = res;
}
void Union(int x, int y, int d) {
int px = Find(x); int py = Find(y);
p[px] = py;
w[px] = (w[y] - w[x] + d + MOD) % MOD;
}
};
void DisjointSet::Init(int n) {
memset(w, 0, sizeof(w));
for (int i = 1; i <= n; i++)
Make(i);
}
int n, m;
DisjointSet set;
int main() {
int tot_case;
scanf("%d", &tot_case);
while (tot_case--) {
scanf("%d%d", &n, &m);
getchar();
set.Init(n);
for (int i = 0; i < m; i++) {
char ch; int a, b;
ch = getchar();
scanf("%d%d", &a, &b);
getchar();
if (ch == 'D') {
set.Union(a, b, 1);
} else {
int pa = set.Find(a);
int pb = set.Find(b);
if (n == 2) {
puts("In different gangs.");
continue;
}
if (pa != pb)
puts("Not sure yet.");
else
puts(set.w[a] == set.w[b] ?
"In the same gang." : "In different gangs.");
}
}
}
return 0;
}