解题思路:并查集经典中的经典题,在网上看了很多大牛的思路,大部分是增加一个结构体存动物间的关系,结合并查集判断,但是关系域的更新比较复杂,一下子不太容易理解。所以就有人另开思路,这里介绍一个十分巧妙的思路。
一般我们都会把一个动物当成一个节点,然后去执行并查集等操作。但是有位大牛另辟蹊径,给每个动物赋予三个节点(n,2n,3n),这样就将并查集的节点数量扩展到3n,用并查集维护这3n个节点的信息就行。下面是具体的思路和操作:
1 先来考虑什么情况下a和b两个动物是同类:
(1)a吃c,b也吃c,则a和b同类;
(2)a吃c,c吃d,d吃b,则a和b同类;
2 有了上面的基础,我们还需要巧妙的定义下面的规则:
(1)如果a和b是同类,则a的三个节点(a_n,a_2n,a_3n)分别指向b的对应的三个节点(b_n,b_2n,b_3n),即a_n -> b_n , a_2n -> b_2n , a_3n -> b_3n;
(2)如果a吃b,则a的三个节点(a_n,a_2n,a_3n)分别指向对应b的三个节点(b_n,b_2n,b_3n)的下一个节点,即a_n -> b_2n , a_2n -> b_3n , a_3n -> b_n;
3 所以我们现在可以表示1中提到的两种同类的情况了:
(1)
(2)
按照上面的规则对每一个动物去建立关系,那么,如果a和b是同类,肯定有a_n与b_n连通,即有相同的根节点;如果a吃b,肯定有a_n与b_2n连通;如果b吃a,肯定有b_n与a_2n连通。
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 300010;
int pre[maxn];
void Init()
{
for(int i = 0;i < maxn; i++)
pre[i] = i;
}
int find(int x)
{
while(x != pre[x])
{
x = pre[x];
}
return x;
}
void merge(int x,int y)
{
int a = find(x);
int b = find(y);
if(a != b)
pre[b] = a;
}
int main()
{
int n,k,d,x,y,flag = 0;
Init();
scanf("%d %d",&n,&k);
for(int i = 0;i < k; i++)
{
scanf("%d %d %d",&d,&x,&y);
if(x > n || y > n || x < 1 || y < 1 || (d == 2 && x == y))
{
flag++;
continue;
}
if(d == 1)
{
if(find(x) == find(y + n) || find(y) == find(x + n))
{
flag++;
continue;
}
else
{
merge(x,y);
merge(x + n,y + n);
merge(x + 2 * n,y + 2 * n);
continue;
}
}
if(d == 2)
{
if(find(x) == find(y) || find(y) == find(x + n))
{
flag++;
continue;
}
else
{
merge(x,y + n);
merge(x + n,y + 2 * n);
merge(x + 2 * n,y);
}
}
}
printf("%d\n",flag);
return 0;
}
标签:食物链,int,1182,节点,merge,POJ,3n,2n,find From: https://blog.51cto.com/u_16131191/6356127