如果只是用普通的并查集,表示两个动物是同类的时候还比较容易,但是对于x吃y这种情况就有点困难。因为并查集是把同类归到一起,对于不同类的处理就没那么容易了,那么我们不妨思考怎么把x吃y的这种情况转化成类似a和b是同类的情况?
思路:
x和y虽然不是同类,但是x吃的类和y是同类,y吃的类和吃x的类也是同类,那么我们就可以将x加上n 表示被x吃的类 x加上2n表示吃x的类,对于y同样处理。那么对于x吃y这种关系我们就可以这么表示:f[x+n]=f[y] f[x+2n] = f[y+n] f[x]=f[y+2n]
注意:本题的输入数据比较大,即使用取消同步的cin也会超时,必须用scanf输入
//poj1182
#include <cstdio>
const int N = 5e4 + 5;
using namespace std;
int f[N * 3]; // x+n表示被x吃的类,x+2*n表示吃x的类
int Find(int x) { return f[x] == x ? x : f[x] = Find(f[x]); }
int main()
{
int cnt = 0;
int n, m, op, x, y, fx[3], fy[3];
scanf("%d%d", &n, &m);
getchar();
for (int i = 3 * n; i > 0; i--)
f[i] = i;
while (m--)
{
scanf("%d%d%d", &op, &x, &y);
getchar();
if (x > n || y > n)
cnt++;
else
{
for (int i = 0; i < 3; i++)
{
fx[i] = Find(x + n * i);
fy[i] = Find(y + n * i);
}
if (op == 1)
{
// x吃y 或 y吃x
if (fx[1] == fy[0] || fy[1] == fx[0])
cnt++;
else
{
for (int i = 0; i < 3; i++)
f[fx[i]] = fy[i];
}
}
else
{
// y吃x 或 x和y相同
if (fy[1] == fx[0] || fx[0] == fy[0])
cnt++;
else
{
f[fx[0]] = f[fy[2]];
f[fx[1]] = f[fy[0]];
f[fx[2]] = f[fy[1]];
}
}
}
}
printf("%d", cnt);
return 0;
}
poj1703的做法与上题相同,并且只有两个状态,更容易一些
//poj1703
#include <cstdio>
const int N = 1e5 + 5;
using namespace std;
int f[2 * N], sz[2 * N]; //x+n表示与x不同的帮派
int Find(int x) { return f[x] == x ? x : f[x] = Find(f[x]); }
int main()
{
int t;
scanf("%d", &t);
getchar();
while (t--)
{
char op;
int a, b, n, m;
scanf("%d %d", &n, &m);
getchar();
for (int i = 2 * n; i >= 0; i--)
{
f[i] = i;
sz[i] = 1;
}
while (m--)
{
scanf("%c %d %d", &op, &a, &b);
getchar();
int fa, fb, fan, fbn;
fa = Find(a);
fb = Find(b);
fan = Find(a + n);
fbn = Find(b + n);
if (op == 'A')
{
if (fa == fb || fan == fbn)
printf("In the same gang.\n");
else if (fa == fbn || fan == fbn)
printf("In different gangs.\n");
else
printf("Not sure yet.\n");
}
else
{
if (sz[fa] > sz[fbn])
{
f[fbn] = f[fa];
sz[fa] += sz[fbn];
}
else
{
f[fa] = f[fbn];
sz[fbn] += sz[fa];
}
if (sz[fan] > sz[fb])
{
f[fb] = f[fan];
sz[fan] += sz[fb];
}
else
{
f[fan] = f[fb];
sz[fb] += sz[fan];
}
}
}
}
return 0;
}