/*
带权并查集是普通并查集的一个拓展。
普通的并查集只是集合,而带权并查集的集合是有关系的集合。集合中的元素有他们之间的相互关系
*/
#include<iostream>
using namespace std;
const int maxn = (5e5 + 7);
int Pre[maxn];//并查集数组
int Rela[maxn];//表示这个点和自己相邻节点的关系,0表示同类,1表示他吃前节点,2便是前节点吃它,为啥这样取,因为这样方便余三
//开始都为同类关系
int find(int x, int &r)//在向前寻找祖先的时候顺便查找他和祖先的关系
{
r = 0;
while (Pre[x] != x)
{
r = (r + Rela[x]) % 3;
x = Pre[x];
}
return x;
}
//同类和捕食操作都会使得集合进行合并
//合并时使得两者与最后祖先的rela关系相同
//捕食的话也可以计算出两者和祖先的关系
//通过带权并查集可以确定集合中任意两个元素之间的关系
//所以在检查结果是否正确时要看一下两者的祖先是否相同
//不相同说明两者关系在之前还没有确定,所以根据情况将两个集合进行合并
//如果相同那么进行核对就行了
int main()
{
int n, k;
cin >> n >> k;
int o, x, y;
int ans = 0;
for (int i = 0; i<maxn; i++)
{
Pre[i] = i;//初始化并查集
}
for (int i = 0; i<k; i++)
{
scanf("%d%d%d", &o, &x, &y);
if (x<1 || y<1 || x>n || y>n)
{
ans++;
}
else
{
int r1, r2;
int ro1 = find(x, r1);
int ro2 = find(y, r2);
int t;//两者关系
if (o == 1)//同类
{
t = 0;//表示同种族
if (ro1 != ro2)//关系未确定
{
//将ro2的父亲职位ro1
//更具Rela
Pre[ro2] = ro1;
//为了使得x,y和最后root的关系相同
Rela[ro2] = (r1 - r2 + 3 + t) % 3;//就是为了让两者和性的root的关系相同
}
else
{
if (r1 != r2)
{
ans++;
}
}
}
else//处于捕食关系
{
t = -1;//表示x捕食y
if (ro1 != ro2)//关系未确定
{
//将ro2的父亲职位ro1
Pre[ro2] = ro1;//
//为了使得x的后和root的关系比y和root的关系大1
Rela[ro2] = (r1 - r2 + 3 + t) % 3;//就是为了让两者和性的root的关系相同
}
else
{
if ((r1 + t + 3) % 3 != r2)
{
ans++;
}
}
}
}
}
cout << ans << endl;
return 0;
}
这题在上一片文章中做过,但不是用带权并查集做的,有点难懂,证明也不好证明。所以又用带权并查集做了一遍。
由于用了新的方法所以应该是算一道新题目吧
还剩995道,加油!!