题意:有n个人和两个阵营,给出两种操作,A a b ,询问a和b的是否在同一个阵营,如果有人未加入任何阵营输入不确定。D a b,让a和b成为不同阵营的。
题解:两种做法。
一种是把并查集扩大一倍,先给每个人一个假定的敌人,然后有D操作时把敌人加入到假定敌人的集合中,A操作判断a和b是否在同一个集合,如果不在判断对方是否在自己假定敌人的集合中,在就是敌人,否则是不确定。
#include <stdio.h>
#include <string.h>
const int N = 100005;
char str[5];
int n, m, pa[N];
int get_parent(int x) {
return x == pa[x] ? x : pa[x] = get_parent(pa[x]);
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &m);
int nn = 2 * n;
for (int i = 0; i <= nn; i++)
pa[i] = i;
while (m--) {
int a, b;
scanf("%s%d%d", str, &a, &b);
int px = get_parent(a);
int py = get_parent(b);
if (str[0] == ‘A‘) {
if (px == py)
printf("In the same gang.\n");
else {
int t1 = get_parent(a + n);
int t2 = get_parent(b + n);
if (px == t2 || py == t1)
printf("In different gangs.\n");
else
printf("Not sure yet.\n");
}
}
else {
int t1 = get_parent(a + n);
int t2 = get_parent(b + n);
if (px != py) {
pa[t2] = px;
pa[t1] = py;
}
}
}
}
return 0;
}
另一种做法是带权并查集,用一个数组rel[i]表示人i和他集合中父亲结点的关系,1表示是同一个阵营,0表示不同阵营。每次找到a和b的祖先与和祖先的关系,然后如果是A操作,如果px==py祖先是同一个人,那么关系就可以确定,否则关系无法确定,如果是D操作,让py成为了px的父亲,就可以更新rel[px],枚举情况发现结果是a和px还有b和py的异或结果。
#include <stdio.h>
const int N = 100005;
char str[5];
int n, m, pa[N];
bool rel[N];
int get_parent(int x, bool &r) {
r = true;
int temp = x;
while (temp != pa[temp]) {
if (rel[temp] == false)
r = !r;
temp = pa[temp];
}
pa[x] = temp;//压缩路径
rel[x] = r;
return temp;
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &m);
for (int i = 0; i <= n; i++) {
rel[i] = true;
pa[i] = i;
}
while (m--) {
int a, b;
scanf("%s%d%d", str, &a, &b);
bool r1, r2;
int px = get_parent(a, r1);
int py = get_parent(b, r2);
if (str[0] == ‘D‘) {
pa[px] = py;
rel[px] = r1 == r2 ? 0 : 1;//异或结果
}
else {
if (px == py) {
if (r1 == r2)
printf("In the same gang.\n");
else
printf("In different gangs.\n");
}
else
printf("Not sure yet.\n");
}
}
}
return 0;
}
时间: 2024-10-15 17:48:41