并查集------E - 食物链

动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是"1 X Y",表示X和Y是同类。
第二种说法是"2 X Y",表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。
Input
第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。
Output
只有一个整数,表示假话的数目。
Sample Input
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
Sample Output
3

这道题是个好题。。。。。
既然要求假话,肯定是说的话跟以前说的存在矛盾,比如A->B,B->A,这样很明显第二句话是假话了,跟第一句话冲突,那么我们应该怎么去查询这种冲突呢,我们知道查询两个节点之间关系最快的方法就是并查集,只要把一些节点归为一个集合,查询起来是非常方便的,那么这道题是不是也可以这么做呢?答案是肯定的,不过我们除了要记录每个节点的根节点以外,还要记录与根节点的关系。
这个博客讲的非常明白了:https://www.cnblogs.com/liuxin13/p/4668205.html
生物之间的关系,0代表二者是同类,1代表A吃B,2代表A被B吃
产生关系(认定为正确的话)的两个动物就进行合并,查询的时候,两个的根节点不相同,说明未产生关系,所以将两个合并;如果两个根节点相同,说明这两个动物之间有关系,所以需要判断是否说的错的,如果(r[y] + d - 1)% 3 != r[x]。。。则说明这个说的是错的,具体原因请看上面博客。。。。。。
主要是并查集如何路径压缩,以及合并,路径压缩时用递归的写法好,主要是计算路径上的r值,合并是合并x与y的根节点即可。。。。
在这里插入图片描述
找出来他们的根节点ra 和 rb
我们想求出来 ra->rb ??
根据向量加法我们得出来
ra->rb = (ra->b+b->rb) % 3
ra->b = (a->b – a->ra + 3) % 3
合并一下两个公式得出来
ra->rb = (a->b - a->ra + 3 + b->rb) %3
有了这个关系我们就可以合并两个集合了,当然在做路径压缩的时候也不能忘了关系压缩。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<cmath>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const double PI = acos(-1);
#define pb push_back
#define mp make_pair
#define fi first
#define se second
const int N = 50005;

int b[N];
//记录与根节点的关系
int r[N];

int join1(int x)
{
    int k = b[x];
    if(b[x] != x){
        b[x] = join1(b[x]);
        r[x] = (r[x] + r[k]) % 3;
    }
    return b[x];
}

int main()
{
    int n,k;
    scanf("%d %d",&n,&k);
    int d,x,y;
    for(int i = 1;i <= n;++i){
        b[i] = i;
        r[i] = 0;
    }
    int sum = 0;
    for(int i = 0;i < k;++i){
        scanf("%d %d %d",&d,&x,&y);
        int p = join1(x),q = join1(y);
        if(x > n || y > n || (d == 2 && x == y)){
            sum++;
            //p == q代表p与q已经产生关系了
        }else if(p == q && (r[y] + d - 1) % 3 != r[x]){
            sum++;
        }else if(p != q){
            b[p] = q;
            r[p] = (d - 1 - r[x] + r[y] + 3) % 3;
        }
    }
    printf("%d\n",sum);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36386435/article/details/82988579