标签:并查集
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