题目: 戳我
题意:城市中有两个社会团伙
已知有n个人:编号为1~n,和m个信息
对于每个信息,D a b表明a和b属于不同的团伙,并且信息不会相互矛盾;A a b表示讯问a和b的关系,
要求依次回答每个讯问,回答形式如下:
Not sure yet. //a和b的关系不确定
In different gans. //a和b属于不同的团伙
In the same gang. //a和b属于同一个团伙
回顾并查集的合并操作:我们总是将同类的集合进行合并,但此题给出的却是不同类的两个元素。
抓住关键,城市的团伙只有两个,这个信息告诉我们:如果a和b不同类,b和c不同类,那么a和c必然属于一个团伙。
更一般的,如果我们把编号看做顶点,关系看做连边。那么,如果顶点a,b之间的路径包含奇数条边,那么a与b属于不同的团伙,如果a,b之间的路径包含偶数条边,那么a与b属于同一个团伙。
我们能够很容易的计算出元素x与根节点的“距离”(即路径上经过的边数)dis[x]。类似的,对于与x同属一个集合的任意元素y,我们也可以计算出y与根节点的“距离”dis[y]。那么,x与y的“距离”便可以用dis[x]+dis[y]表示。(注意,我们只关心“距离”的奇偶性,并不关心“距离”是否是最短的)
代码:
#include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <stack> #include <map> using namespace std; const int inf = 0x3f; const int INF = 0x3f3f3f3f; const int maxn = 1e5+5; int bleg[maxn], dis[maxn], n, q; int find(int x) { int y = x, cnt = 0; while(y != bleg[y]) { cnt += dis[y]; y = bleg[y]; } while(x != bleg[x]) { int px = bleg[x], tmp = dis[x]; dis[x] = cnt; bleg[x] = y; cnt -= tmp; x = px; } return y; } void Union(int a, int b) { int pa = find(a), pb = find(b); if(pa == pb) return; dis[pa] = dis[a] + dis[b] + 1; bleg[pa] = pb; } void Init() { for(int i = 0; i <= n; i++) { bleg[i] = i; dis[i] = 0; } } int main() { int T; scanf("%d", &T); while(T--) { scanf("%d %d", &n, &q); Init(); while(q--) { int x, y; char op; scanf(" %c %d %d", &op, &x, &y); if(op == 'A') { int pa = find(x), pb = find(y); if(pa != pb) { puts("Not sure yet."); } else if((dis[x] + dis[y]) % 2 != 0) { puts("In different gangs."); //printf("%d %d\n", dis[x], dis[y]); } else { puts("In the same gang."); } } else { Union(x, y); } } } return 0; }