题意:Bug有两种性别,异性之间才交往, 让你根据数据判断是否存在同性恋,输入有 t 组数据,每组数据给出bug数量n, 和关系数m, 以下m行给出相交往的一对Bug编号 a, b。只需要判断有没有,按题目要求输出。这题有点坑的地方在于输出上多了一行空行,不PE都没注意到。
思路:
用一个数组gender[i] 记录当前节点 i 与根节点的关系,parent[i]数组记录当前节点的父节点。 因为是带权并查集,在Find_Parent 时更新当前节点与根节点的关系,且路径压缩至根节点下, 所以不用像普通并查集一样开辟辅助路径压缩的数组,合并的时候不用路径压缩。
由于只有同性、异性两种关系,所以用gender[]数组存0、1表示两种性别。根节点相同的且性别相同的则是同性恋。
然后下面说说不好理解的 Find_parent和合并时 对gender[] 的更新。
首先,初始化的时候所有节点的父节点都是自己,gender[]都为0。又因为合并时无需路径压缩,所以根节点的gender始终为0,这是推导其他子节点关系的关键。
Find_parent:由根节点始终为0,我们可以得到在Find_Parent时当前节点 i 与根节点的更新公式:gender[i] = gender[i] ^ gender[ par[i] ] (^为异或). 即 i 和 par[i] 性别相同 则 i 和根节点是异性,否则 i 和根节点则为同性。
合并操作:合并x, y,找到x, y的父节点a, b,合并par[a] = b, 作为 子节点的那个父节点对根节点的关系 由gender[x] 和 gender[y]决定, 由于多加了一条边,所以gender[a] = (gender[x] + gender[y] +1)%2.
判断同性恋:Union()返回布尔值,如果x, y父节点相同且性别相同则直接返回true表示找到同性恋,否则返回false。其他合并操作如上。
AC代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 #define maxn 2006 5 int gen[maxn], par[maxn]; 6 int Find(int x) 7 { 8 if(par[x] == x) return x; 9 int t = Find(par[x]); 10 gen[x] = gen[x]^gen[par[x]]; 11 return par[x] = t; 12 } 13 bool Union(int x, int y) 14 { 15 int a = Find(x); 16 int b = Find(y); 17 if(a == b){ 18 if(gen[x] == gen[y]) return true; 19 return false; 20 } 21 par[a] = b; 22 gen[a] = (gen[x]+gen[y]+1)%2; 23 return false; 24 } 25 26 int main() 27 { 28 int t; cin>>t; 29 for(int z = 1; z <= t; z++){ 30 int flag = 0; 31 int n, m; scanf("%d%d", &n, &m); 32 for(int i = 1; i <= n; i++) par[i] = i, gen[i] = 0; 33 34 for(int i = 0; i < m; i++){ 35 int a, b; scanf("%d%d", &a, &b); 36 if(flag) continue; 37 flag = Union(a, b); 38 } 39 40 if(flag) 41 printf("Scenario #%d:\nSuspicious bugs found!\n\n", z); 42 else printf("Scenario #%d:\nNo suspicious bugs found!\n\n", z); 43 } 44 }