并查集--食物链

食物链
动物王国中有三类动物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),输出假话的总数。

给你 N N N种动物,但是它的种类不知道,所以都需要维护。
假设动物 x x x,用 f a [ x ] fa[x] fa[x]来维护x为A的信息, f a [ x + n ] fa[x+n] fa[x+n]来维护x为B的信息,用 f a [ x + 2 ∗ n ] fa[x+2*n] fa[x+2n]来维护x为C的信息。

如果题目说法为x吃y,那么什么时候为假话?

  • x和y是同类
  • y吃x

如果题目说法为x和y是同类,那么什么时候是假话?

  • x吃y
  • y吃x

如果x吃y,那么就需要考虑三种情况:

  1. x是A,那么unite(x,y+n)
  2. x是B,那么unite(x+n,y+2*n)
  3. x是C,那么unite(x+2*n,y)

如果x和y是同类,那么只需要:

unite(x,y)
unite(x+n,y+n)
unite(x+2*n,y+2*n)

考虑完毕,给出代码,

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int MAX_N = 5e4 + 10;
const int INF = 0x3f3f3f3f;
#define mem(x) memset(x, 0, sizeof(x))
#define sz(x) (int)x.size()
#define pb(x) push_back(x)
#define all(x) x.begin(), x.end()
#define sb(x) (x) * (x)
#define mkp make_pair
#define _rep(i, a, b) for (int i = a; i <= b; ++i)
#define _for(i, a, b) for (int i = a; i < b; ++i)
#define endl '\n'
#define LOCAL

int n, m;
int fa[MAX_N * 3], rk[MAX_N * 3];

int find_root(int x) {
    if (fa[x] == x) {
        return x;
    } else {
        return fa[x] = find_root(fa[x]);
    }
}

void unite(int x, int y) {
    x = find_root(x), y = find_root(y);
    if (x == y) return;
    if (rk[x] < rk[y]) {
        fa[x] = y;
    } else {
        fa[y] = x;
        if (rk[x] == rk[y]) rk[x]++;
    }
}

bool same(int x, int y) { return find_root(x) == find_root(y); }

int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    scanf("%d%d", &n, &m);
    int ans = 0;
    for (int i = 0; i < n * 3; ++i) {
        fa[i] = i;
        rk[i] = 0;
    }
    while (m--) {
        int D, x, y;
        scanf("%d%d%d", &D, &x, &y);
        x--, y--;
        if (x < 0 || y < 0 || x >= n || y >= n || (D == 2 && x == y)) {
            ans++;
            // cerr << D << " " << x << " " << y << endl;
            continue;
        }
        if (D == 2) {
            if (same(x, y) || same(x + n, y + n) ||
                same(x + 2 * n, y + 2 * n) || same(y, x + n) ||
                same(y + n, x + 2 * n) || same(y + 2 * n, x)) {
                ans++;
                // cerr << D << " " << x << " " << y << endl;
            } else {
                unite(x, y + n);
                unite(x + n, y + 2 * n);
                unite(x + 2 * n, y);
            }
        } else {
            if (same(x, y + n) || same(x + n, y + 2 * n) ||
                same(x + 2 * n, y) || same(y, x + n) ||
                same(y + n, x + 2 * n) || same(y + 2 * n, x)) {
                ans++;
                // cerr << D << " " << x << " " << y << endl;
            } else {
                unite(x, y);
                unite(x + n, y + n);
                unite(x + 2 * n, y + 2 * n);
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值