题意:
有两黑帮,N个人,现在又一些信息,你需要根据这些信息确定这两个人之间是否属于同一个黑帮。有两种输入: D a b 表示a,b不属于同一个黑帮;A a b 询问a,b是否属于同一个黑帮,可以回答属于,不属于,不确定三种。
题解:
可以用带权的并查集解决。若a,b同属于一个集合说明二者的关系已经确定,输出属于或者不属于;否则输出不确定。当a,b同属于一个集合时,可以通过权值来判断它们的具体关系。注意:这里的权值仅仅代表某个节点在压缩森林中与其根节点的距离,并不代表它在原森林中的深度。
#include<cstdio>
using namespace std;
#define MAX 200000
int father[MAX], weight[MAX], rank[MAX];
bool check[MAX];
void init ( int n )
{
for ( int i = 1; i <= n; i++ )
{
father[i] = i;
weight[i] = 0;
rank[i] = 1;
check[i] = false;
}
}
int Find ( int x, int& w ) //节点到压缩森林中根节点的距离 = 父节点到根的距离 + 本身到父节点的距离
{
if ( father[x] != x )
{
father[x] = Find ( father[x], w );
weight[x] = (weight[x] + w) % 2;
w = weight[x];
}
else w = 0;
return father[x];
}
int Find_Depth ( int x )
{
int a, w;
if ( x == father[x] ) //根节点到自己的距离为0,直接返回
return weight[x];
a = Find ( x, w );
return weight[a] + weight[x]; //节点到压缩森林中根节点的距离 = 父节点的距离 + 本身到父节点的距离
}
void Link ( int x, int y ) //按秩结合
{
int a, b, w;
a = Find ( x, w );
b = Find ( y, w );
if ( a == b ) return;
if ( rank[a] >= rank[b] )
{
weight[b] = (weight[x] + 1 + weight[y]) % 2;
rank[a] += rank[b];
father[b] = a;
}
else
{
weight[a] = (weight[x] + 1 + weight[y]) % 2;
rank[b] += rank[a];
father[a] = b;
}
}
int main()
{
char ch[4];
int n, m, a, b, w, cs;
scanf("%d",&cs);
while ( cs-- )
{
scanf("%d%d",&n,&m);
init ( n );
while ( m-- )
{
scanf("%s %d %d",ch, &a, &b);
if ( ch[0] == 'D' )
Link ( a, b );
else
{
if ( Find(a,w) != Find(b,w) )
printf("Not sure yet.\n");
else if ( Find_Depth(a) % 2 == Find_Depth(b) % 2)
printf("In the same gang.\n");
else
printf("In different gangs.\n");
}
}
}
return 0;
}
在网上还看到另一种解法:
#include <iostream>
using namespace std;
#define N 100005
int opp[N], father[N], rank[N];
int n, m;
void make_set ( int x )
{
for ( int i = 0; i <= x; ++i )
{
father[i] = i;
opp[i] = rank[i] = 0;
}
}
int find_set ( int x )
{
if ( father[x] != x )
father[x] = find_set(father[x]);
return father[x];
}
void Union ( int x, int y )
{
int fx = find_set(x);
int fy = find_set(y);
if ( fx == fy ) return;
if ( rank[fx] > rank[fy] )
father[fy] = fx;
else
father[fx] = fy;
if ( rank[fx] == rank[fy] )
rank[fy]++;
}
int main()
{
int t, n, m, x, y;
char ask;
scanf("%d",&t);
while ( t-- )
{
scanf("%d%d",&n,&m);
make_set(n);
while ( m-- )
{
getchar();
scanf("%c",&ask);
scanf("%d%d",&x,&y);
if ( ask == 'A' )
{
if ( find_set(x) == find_set(y) )
printf("In the same gang.\n");
else if ( find_set(x) == find_set(opp[y]) )
printf("In different gangs.\n");
else
printf("Not sure yet.\n");
}
else
{
if ( opp[x] == 0 && opp[y] == 0 )
{
opp[x] = y;
opp[y] = x;
}
else if ( opp[x] != 0 && opp[y] == 0 )
{
opp[y] = x;
Union(y,opp[x]);
}
else if ( opp[x] == 0 && opp[y] != 0 )
{
opp[x] = y;
Union(x,opp[y]);
}
else if ( opp[x] != 0 && opp[y] != 0 )
{
Union(x,opp[y]);
Union(y,opp[x]);
}
}
}
}
return 0;
}