还没有很好的理解,先把AC代码段贴在这里,再慢慢消化。总体思路是用fat[i]存i个动物的父节点,ran[i]存i和父亲的关系:0表示同类,1表示吃父亲,2表示被父亲吃。首先初始化所有动物节点的父节点为本身,(fat[i] = i)。和父亲的关系为同类(ran[i] = 0)。然后接受输入,首先查询输入的根节点是否相同(即判断是否属于同一棵树)(查询的同时进行了路径压缩),属于同一棵树就判断这句话是真是假,即(ran[x] - ran[y] + 3) % 3 == typ - 1 (1)为真则这句话为真,否则为假。
若查询的根节点不同(即不属于同一颗树),那么这句话和前面的就不矛盾,合并x和y所在的树。即 fat[x1] = y1
ran[x1] = (-ran[x] + typ - 1 + ran[y] + 3) % 3; (2)
这里这两个式子的获得有一定的难度
/*n个动物 k句话 有一种循环a吃b 吃c c吃a 开始不知道n种动物关系是什么
两种询问:d=1 x y为同类 d=2 x吃y 判断假话条数(关键之违背之前的关系)
并查集可以很好解决的满足区间传递关系的区间合并问题,注意一般是多棵树*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int Max = 50010;
int fat[Max], ran[Max];
void Init(int n)//初始化重要
{
for (int i = 0; i <= n; i++)
{
fat[i] = i;//初始化都是指向(看做)自己
ran[i] = 0;//0同类 1吃父节点 2被父节点吃
}
return;
}
int Find(int x)//找寻父节点+路径压缩
{
if (x == fat[x])
return fat[x];
int y = Find(fat[x]);
ran[x] = (ran[x] + ran[fat[x]]) % 3;//递归后从祖先节点向后到每个孩子来计算,看和根节点是吃还是被吃的关系
fat[x] = y;//路径压缩
return y;
}
int Union(int typ, int x, int y)//区间并与查询
{
int x1 = Find(x);
int y1 = Find(y);
if (x1 == y1)//属于同一棵树(共父节点),判断出关系
{
if ((ran[x] - ran[y] + 3) % 3 == typ - 1)//ran[x]和ran[y]都是跟根节点的关系,ran[x]=ran[y]则同类,ran[x]=ran[y]+1
return 0;
else return 1;
}
fat[x1] = y1;//连接两父节点
ran[x1] = (-ran[x] + typ - 1 + ran[y] + 3) % 3;//使用类似向量方法来计算权值,虽然题目只有两个,但是会出现被吃这种情况,所以要变成3种情况,注意一定要处理负数的情况
return 0;
}
int main()
{
int n, k, ans;
int typ, smt1, smt2;
scanf("%d %d", &n, &k);
Init(n);
ans = 0;
for (int i = 0; i<k; i++)
{
scanf("%d %d %d", &typ, &smt1, &smt2);
if (smt1 == smt2&&typ == 2)
ans++;
else if (smt1>n || smt2>n)
ans++;
else
ans += Union(typ, smt1, smt2);
}
printf("%d\n", ans);
return 0;
}