并查集拓展域

并查集拓展域

并查集是用来维护元素之间的制约关系,并且在更新时可以传递关系的一种数据结构,但有时候只靠是否在集合内不足以表示复杂的关系,因此引入了拓展域的关系.

例题 食物链

1. 带权并查集

#include <iostream>
#include <numeric>
#include <functional>
using namespace std;

const int N = 50010;
int p[N], d[N];
/*
    d(i) 表示 i 与父结点的距离。
    1 -> i可以吃父结点
    2 -> i可以被父结点吃
    0 -> i与父结点同类
*/

int main ()
{
    function<int(int)> f = [&](int x) {
        if (x != p[x])
        {
            // 维护d数组
            int u = f(p[x]);
            d[x] += d[p[x]];
            p[x] = u;
        }
        return p[x];
    } ;
    // mg 对于同类和不同类的情况不同
    
    int n, k, od, x, y, ret = 0; cin >> n >> k;
    iota(p+1, p+n+1, 1);
    while(k -- && cin >> od >> x >> y)
    {
        if (x > n || y > n) { ++ ret; continue; }
        int px = f(x), py = f(y);
        if (od == 1)
        {
            if (px == py && (d[x] - d[y]) % 3) { ++ ret; continue; }
            // 加入一个集合
            if (px != py)
            {
                p[px] = py;
                d[px] = d[y] - d[x];
            }
        }
        else
        {
            if (x == y || px == py && (d[x] - d[y] - 1) % 3) { ++ ret; continue; }
            if (px != py)
            {
                // 加入一个集合
                p[px] = py;
                d[px] = d[y] + 1 - d[x];
            }
        }
    }
    cout << ret << endl;
    return 0;
}

2. 拆点并查集(拓展域)

#include <iostream>
#include <numeric>
#include <functional>
using namespace std;

const int N = 200010;
int p[N]; // 拆点,每个点有三个域,天敌域,同类域,捕食域
/*
    x         : 维护x的所有天敌
    x + n     : 维护x的所有同类
    x + 2 * n : 维护x的所有捕食
*/

// 每个集合里的元素都是一类

int main ()
{
    function<int(int)> f = [&](int x) { return x == p[x] ? x : p[x] = f(p[x]); } ;
    function<void(int, int)> mg = [&](int x, int y) { p[f(x)] = f(y); };
    
    int n, k, d, x, y, ret = 0; cin >> n >> k;
    iota(p + 1, p + 3 * n + 1, 1);
    while(k -- && cin >> d >> x >> y)
    {
        if (x > n || y > n) { ret ++ ; continue; } // 假话2
        if (d == 2 && x == y) { ret ++ ; continue; } // 假话3
        // 假话1,产生冲突
        if (d == 1)
        {
            // 如果判定x和y是同类,但是x的天敌域或者捕食域有y,一定是假话
            if (f(x) == f(y + n) || f(x + 2 * n) == f(y + n)) { ret ++ ; continue; }
            // 否则这是一句真话,记录一下
            mg(x + n, y + n);
            mg(x, y); // x的天敌和y的天敌是同一类
            mg(x + 2 * n, y + 2 * n); // x的捕食域和y的捕食域也是一类
        }
        else
        {
            // 如果判定x捕食y,但是x同类域或天敌域有y,假话
            if (f(x + n) == f(y + n) || f(x) == f(y + n)) { ret ++ ; continue; }
            mg(x + 2 * n, y + n);
            mg(x + n, y); // y的天敌域加上x的同类域
            mg(x, y + 2 * n); // x的天敌域加上y的捕食域
            
        }
    }
    cout << ret << endl;
    return 0;
}
posted @ 2021-11-20 14:27  Horb7  阅读(69)  评论(0编辑  收藏  举报