1.题目描述:点击打开链接
2.解题思路:本题利用并查集解决。根据题意知,需要三个并查集,这里为了方便,给数组par均分为三段,分别记为A,B,C。通过下标来控制不同的集合。
那么接下来该如何处理每一条信息呢?当输入第一种信息时,表示u和v属于同一类,此时有三种可能,因此要把u和v分别合并到A,B,C中。如果是第二种,表示u吃v,那么也有三种情况:u在A中v在B中,u在B中v在C中,u在C中v在A中。
那么还有一个问题,如何判断信息是否合法呢?由于我们是把一个数组切割成了三大段,那么A,B,C其实是在同一组中的,而同一组中的所所有元素代表的情况要么同时发生,要么都不发生。比如如果u属于A和v属于B在同一个组里,那么如果u属于A为真,那么必有v属于B为真,反之同理。所以,只用判断u,v是否在同一个组中即可知该句话为真还是为假。
本题需要对并查集的性质深刻理解才能正确解题。
3.代码:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;
#define N 150000+10
int par[N], rnk[N];
int n, m;
void init(int n)
{
for (int i = 0; i < n; i++)
{
par[i] = i;
rnk[i] = 0;
}
}
int find(int x)
{
if (par[x] == x)return x;
else return par[x] = find(par[x]);
}
bool same(int x, int y)
{
return find(x) == find(y);
}
void unit(int x, int y)
{
x = find(x); y = find(y);
if (x == y)return;
if (rnk[x] < rnk[y])par[x] = y;
else
{
par[y] = x;
if (rnk[x] == rnk[y])rnk[x]++;
}
}
int main()
{
//freopen("t.txt", "r", stdin);
scanf("%d%d", &n, &m);
init(3 * n);
int cnt = 0;
for (int i = 1; i <= m; i++)
{
int id, u, v;
scanf("%d%d%d", &id, &u, &v);
u--, v--;
if (u >= n || v >= n || v < 0 || u < 0)cnt++;//编号非法
else if (id == 1)
{
if (same(u, v + n) || same(u, v + 2 * n))cnt++;//u和v实际上不属于同一个种类
else
{
unit(u, v); unit(u + n, v + n); unit(u + 2 * n, v + 2 * n);//3种可能的情况
}
}
else
{
if (same(u, v) || same(u, v + 2 * n))cnt++;//u和v是同一类 或者 实际上是v吃u(三种情况中只用挑一种即可)
else
{
unit(u, v + n); unit(u + n, v + 2 * n); unit(u + 2 * n, v);
}
}
}
printf("%d\n", cnt);
return 0;
}原文:http://blog.csdn.net/u014800748/article/details/44907767