//940K 469MS C++
// cin TLE...
#include <cstdio>
#include <cstring>
#include <cassert>
#include <iostream>
using namespace std;
const int MAX = 100100;
int UF_set[MAX];
int hate[MAX];
int caseNum;
int criminalNum;
int messageNum;
void UF_getSetId(int curId) {
if (UF_set[curId] == 0) {
return;
}
int topId = UF_set[curId];
while(UF_set[topId] != topId) {
topId = UF_set[topId];
}
UF_set[curId] = topId;
}
void UF_fill(int A, int B) {
UF_getSetId(A);
UF_getSetId(B);
int AsetId = UF_set[A];
int BsetId = UF_set[B];
if (!AsetId && !BsetId) {
// A and B first filled, belong to different set and hate each other.
UF_set[A] = A;
UF_set[B] = B;
hate[A] = B;
hate[B] = A;
} else if (AsetId && !BsetId) {
// A has been filled, B not, B is added in the set which hated by A'set
// also update hate[];
int ASetHate = hate[AsetId];
UF_set[B] = ASetHate;
hate[A] = hate[AsetId];
hate[B] = AsetId;
} else if (!AsetId && BsetId) {
// B has been filled, A not, A is added in the set which hated by B'set
// also update hate[];
int BSetHate = hate[BsetId];
UF_set[A] = BSetHate;
hate[A] = BsetId;
hate[B] = hate[BsetId];
} else if (AsetId && BsetId) {
// A and B all filled before, should not in same set(becasue A and B hate each other)
// assert(AsetId != BsetId);
// get A's set's hate set, add B's set in this set.
// and add B's set's hate set in A set.
int ASetHate = hate[AsetId];
if (ASetHate != BsetId) {
int BSetHate = hate[BsetId];
hate[BsetId] = AsetId;
UF_set[BsetId] = ASetHate;
hate[B] = AsetId;
UF_set[BSetHate] = AsetId;
hate[BSetHate] = ASetHate;
}
}
}
void ifSameGang(int A, int B) {
UF_getSetId(A);
UF_getSetId(B);
int AsetId = UF_set[A];
int BsetId = UF_set[B];
// if A or B never filled
if (!AsetId || !BsetId) {
printf("Not sure yet.\n");
} else if (AsetId == BsetId) {
printf("In the same gang.\n");
} else {
if (hate[AsetId] == BsetId) {
printf("In different gangs.\n");
} else {
printf("Not sure yet.\n");
}
}
}
char message[100];
int main() {
scanf("%d", &caseNum);
for (int i = 1; i <= caseNum; i++) {
memset(UF_set, 0, sizeof(UF_set));
memset(hate, 0, sizeof(hate));
scanf("%d %d", &criminalNum, &messageNum);
for (int i = 1; i <= messageNum; i++) {
int A;
int B;
// cin>>message>>A>>B;
scanf("%s", message);
if (message[0] == 'D') {
scanf("%d %d", &A, &B);
// printf("%c %d %d\n", message, A, B);
UF_fill(A, B);
} else if (message[0] == 'A') {
scanf("%d %d", &A, &B);
// printf("%c %d %d\n", message, A, B);
ifSameGang(A, B);
}
}
}
}
一道并查集的中级应用题, 这道题的最大难点是,对于给定的一组输入, 除了知道两者是对立的以外,并不知道谁到底属于哪一方,
A与B对立,但是A和B的归属会有两种情况:
A属于dragon, B属于snake
A属于snake, B属于dragon,
用并查集显然无法表示这种模棱两可的情况,因此这种情况下,就不急着将A和B直接归类到dragon/snake. 而是令其现自立门派,分别属于自己的gang,即并查集中
A和B分别是两个set,且setId分别是A和B,
具体的操作例子:
当前加入了
A <->B,
C<->D,
那么现在set的情况是: 一共有4个set,set的根分别就是 A,B,C,D,接下来加入新的对立关系,共有这些case:
case1: E<->F, E 和 F和之前加入的A B C D都没有关系,那么继续E和F建立自己的帮派, 一共有6个set了。
case2: A <->E, 因为已经知道A和B是对立的,那么E和B必然是一个帮派的,那么就将E加入到B的帮派中。
case3: B<->C, 因为知道 B和A对立,那么C必然和A一个帮派,那么将C加入到A帮派中, 而D和C对立,也就是和AC帮派对立,那么D一定和B一个帮派,将D加入到B帮派中。
就这样操作,
最后会会得到一些集合(因为题目并不保证最后一定会将子帮派彻底合并成dragon和snake, 比如,最后可能就是 A <->B. C<->D这个四个帮派),并且还要维护一个对立列表hate来记录对于某个帮派,其对立帮派是哪个,这样才能正确的判断,某两人是是对立还是不能确定。
在query对立关系的时候,如果A和B都属于一个帮派,那么好办,
如果不属于一个帮派,就需要检查所属于的两个帮派是否对立了.