本题为二分的并查集,其实只要在原先的并查集基础上作一下变形。当然此题也还是有技巧的,我们可以对每个节点做个标记,若该节点与父亲节点属于同一类,则标记为,0,否则标记为1。但当我们合并两棵树时,可能会存在两棵树的标记所表达的意思完全相反,此时我们就要通过改变其中一棵树根节点的标记,来保持合并之后的树保持一致。
至于何时有同性恋发生,应该不难判断了,若两个节点属于同一棵树且属于同一类则发生同性恋。
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=2005;
int set[maxn],flag[maxn];
//set[i]记录i的父亲节点,flag[i]==0表示该节点的性别与父亲节点相同,否则不相同
void init(int n)
{
for(int i=1;i<=n;i++)
{
set[i]=i;
flag[i]=0;
}
}
int find1(int x)//返回父亲节点
{
while(set[x]!=x)
x=set[x];
return x;
}
int find2(int x)//返回0或1,判断该节点与根节点是否一致
{
int t=0;
while(set[x]!=x)
{
t^=flag[x];
x=set[x];
}
return t;
}
bool merge(int x,int y)//合并
{
int f1=find1(x);
int f2=find1(y);
if(f1==f2)
return false;
set[f1]=f2;
//如果两棵树定义有冲突,则使它们一致(此处通过改变flag[f1])
if(find2(x)==find2(y))
flag[f1]^=1;
return true;
}
int main()
{
int t,n,m,a,b,C=1;
bool state;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
init(n);
state=true;
while(m--)
{
scanf("%d%d",&a,&b);
if(!state)
continue;
if(!merge(a,b)&&(find2(a)^find2(b))==0)//同一棵树且性别相同的两个节点
state=false;
}
printf("Scenario #%d:\n",C++);
if(!state)
printf("Suspicious bugs found!\n");
else
printf("No suspicious bugs found!\n");
printf("\n");//每个样例后面都换行,坑爹的地方
}
return 0;
}