poj-1703

来源:互联网 发布:淘宝端口急速退款 编辑:程序博客网 时间:2024/05/16 18:17
//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都属于一个帮派,那么好办,

如果不属于一个帮派,就需要检查所属于的两个帮派是否对立了.

0 0
原创粉丝点击