POJ-1182-食物链
这道题是并查集的经典题型。
难度高于模板裸题。但是又在可控的范围内。
中文题就不描述啦~
结构体中记录结点信息:p[i].fa记录父结点,p[i].rela记录与父结点的关系。
我们路径压缩的时候更新父结点为根结点。这点是很显然的。
首先进行归定:
p[x].rela = 0;代表与父结点是同类关系。
p[x].rela = 1,代表与父结点的关系:父结点吃x
p[x].rela = 2;代表与父结点的关系:x吃父结点
特判:
1.当输入的a, b任意一个大于给定n的时候就让sum++.(谎话++)
2.当op = 2 && a == b的时候。不允许存在a吃自己的情况。sum++
我们再方位到a, b的根结点。
判断根结点是否相同。
不相同进行mix合并。
相同的话:
特判情况:如果op == 1;属于相同种类时,他们与根结点的关系不一样。sum++
还有一种特判情况:op == 2是被吃的情况下。需要用式子来判断。
这里关键是三个式子需要注意:
这里逐一解释:
find()部分:
p[x].rela = (p[t].rela + p[x].rela) % 3;
t代表x原来的父结点
这个式子是把当前结点的关系更新
因为父结点已经路径压缩为根结点了。
这里枚举一两个式子可以得到上面的关系式:
比如:
当p[x].rela = 1;x与原父结点t是:t吃x
p[t].rela = 0;原父结点t和根结点是:同类关系。
所以x与根结点的关系:根结点吃x;更新为1.
(找数字规律找出来的。没有什么方法。。emmm。就是枚举找规律。。)
通过枚举我们可以总结出是两者的关系相加之后模3的关系(为啥是模3.因为按照先开始的规定就只有0,1,2这三个数。)
当判断两个根结点不相同,需要进行合并的时候:
我们把b的根结点root2更新为a的根结点root1.
那么root2的rela也需要进行更新:
这个地方的式子也是通过枚举找规律发现的:
p[root2].rela = (p[a].rela - p[b].rela + (op == 1 ? 0 : 1) + 3) % 3;
假设op = 1的情况下:a和b是同类情况:
p[a].rela = 0, p[b].rela = 0;
我们可以得到p[root2].rela = 0;
p[a].rela = 0, p[b].rela = 1;
我们可以得到p[root2].rela = 2;
p[a].rela = 0, p[b].rela = 2;
我们可以得到p[root2].rela = 1;
p[a].rela = 1, p[b].rela = 0;
我们可以得到p[root2].rela = 1;
…模拟完op = 1之后模拟op = 2的情况:
p[a].rela = 0, p[b].rela = 0;
我们可以得到p[root2].rela = 1;
我们可以发现op=1的时候是a的rela-b的rela然后模3得到的。
我们还可以发现op = 2的情况是在op = 1的情况上+1模3得到的。
所以式子就出来了
还有一个式子是特判情况:op == 2是被吃的情况下:
判断不符合已经构造好的关系的情况:
方法也是枚举:(枚举符合要求的情况)
前提条件:a和b同根。a吃b
当p[a].rela = 1时:p[b].rela应该等于2
…这样枚举下来;
我们可以得到b的rela-a的rela模3等于1需要成立。如果不成立说明矛盾:
式子:
p[b].rela - p[a].rela + 3) % 3
这个解决完了就很简单了
代码部分:
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 5e4 + 10;
struct node
{
int fa;
int rela;
}p[N];
int n, k;
int op;
int a, b;
int sum;
void init()
{
for (int i = 1; i <= n; i++)
{
p[i].fa = i;
p[i].rela = 0;
}
}
int find(int x)
{
if (x != p[x].fa)
{
int t = p[x].fa;
p[x].fa = find(p[x].fa);
p[x].rela = (p[t].rela + p[x].rela) % 3;
return p[x].fa;
}
return p[x].fa;
}
int main()
{
cin >> n >> k;
init();
for (int i = 1; i <= k; i++)
{
scanf ("%d%d%d", &op, &a, &b);
if (a > n || b > n)
{
sum++;
continue;
}
if (op == 2 && a == b)
{
sum++;
continue;
}
int root1 = find(a);
int root2 = find(b);
if (root1 != root2)
{
p[root2].fa = root1;
p[root2].rela = (p[a].rela - p[b].rela + (op == 1 ? 0 : 1) + 3) % 3;
}
else
{
if (op == 1 && p[a].rela != p[b].rela)
{
sum++;
}
else if (op == 2 && (p[b].rela - p[a].rela + 3) % 3 != 1)
{
sum++;
}
}
}
cout << sum << endl;
return 0;
}