题目链接:http://poj.org/problem?id=1703
这道题和食物链那道题有异曲同工之处,都是要处理不同集合之间的关系,而并查集的功能是维护相同集合之间的关系。这道题中有两个不同的集合,朴素并查集只能查询两者是否属于同一个集合,扩展并查集可以建立多个集合之间的关系。
本题我看了很多博客,对于两个集合,有许多博客都是采取2*n大小的并查集解决。大家的说法都是1-n属于一个集合,n-2*n属于另一个集合,我看的云里雾里,下面我想用我的方法来证明这样划分两个集合的正确性。
证明:我们人为上的操作既然不会将k和n+k结点合并就说明k结点和n+k结点永远也不会在同一个集合中,我们可以认为(k,n+k)是一组对立元组,我们要将(a,b)元组中的a和b归到不同的集合中去,由于(a,n+a)和(b,n+b)一定是对立的元组,现在我们要制造(a,b)是对立元组的局面,我们可以知道(a,n+b)属于同一个集合和(a+n,b)属于同一个集合。现在不用管两个到底具体属于哪个集合,只要知道他们是属于不同的集合,所以只要将(a,n+b)合并还有(a+n,b)合并就可以保证(a,b)是对立元组。查询时也只要看(a,b)是否属于同一个集合。证毕。
根据我的证明,大家应该对基础的种类并查集有了一点认识。代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 5 using namespace std; 6 const int maxn = 1e5 + 5; 7 8 int n, m; 9 int par[maxn*2], high[maxn*2]; 10 11 void init(int n) 12 { 13 for(int i = 1; i <= n; i++) 14 { 15 par[i] = i; 16 high[i] = 0; 17 } 18 } 19 20 int getRoot(int x) 21 { 22 return par[x] == x ? x : par[x] = getRoot(par[x]); 23 } 24 25 void unite(int x, int y) 26 { 27 x = getRoot(x); 28 y = getRoot(y); 29 if(x == y) return; 30 31 if(high[x] < high[y]) par[x] = y; 32 else 33 { 34 par[y] = x; 35 if(high[x] == high[y]) high[x]++; 36 } 37 } 38 39 bool same(int x, int y) 40 { 41 return getRoot(x) == getRoot(y); 42 } 43 44 int main() 45 { 46 //freopen("in.txt", "r", stdin); 47 int T; 48 scanf("%d", &T); 49 while(T--) 50 { 51 scanf("%d%d", &n, &m); 52 init(2*n); 53 54 char op; int a, b; 55 for(int i = 1; i <= m; i++) 56 { 57 scanf(" %c%d%d", &op, &a, &b); 58 if(op == 'D') 59 { 60 unite(a, b+n); 61 unite(a+n, b); 62 } 63 else 64 { 65 if(same(a, b+n) || same(a+n, b)) printf("In different gangs. "); 66 else if(same(a, b) || same(a+n, b+n)) printf("In the same gang. "); 67 else printf("Not sure yet. "); 68 } 69 } 70 } 71 return 0; 72 }