并查集拓展域
并查集拓展域
并查集是用来维护元素之间的制约关系,并且在更新时可以传递关系的一种数据结构,但有时候只靠是否在集合内不足以表示复杂的关系,因此引入了拓展域的关系.
例题 食物链
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;
}