Sample Input
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
Sample Output
3
前几天做题的时候,做到了种类并查集,于是决定把以前学的“食物链”也解决掉,这两种题都是普通并查集的进阶,做起来挺有意思的。
https://blog.csdn.net/niushuai666/article/details/6981689这篇博客将的特别详细,但看的过程中遇到了一些问题,所以写一下自己的思路。
与普通的并查集相比,需要开一个rela[maxn]数组来保存与父亲结点的关系。
就此题而言生物间共存在三种关系:
rela[x] = 0 ……表示节点x与其父节点rootx的关系是:同类
rela[x] = 1 ……表示节点x与其父节点rootx的关系是:被根结点吃
rela[x] = 2 ……表示节点x与其父节点rootx的关系是:吃根结点
(因为生物间共存在三种关系,而D只有两种状态,无法表示出生物间的关系,所以要自己找出一组数与D有联系的数)
此处规定关系为 D = D-1(D为输入的关系),(可能你会认为这样操作会使关系改变,那么请看下面蓝体字(前提是懂得了并查集中内在偏移量的关系,如果不懂请看链接))
并查集在查找过程中要进行数据压缩,父亲结点会改变!!而rela[x]是x与父结点的关系,所以要更新!!!
关系更新要进行两次:
1.原来没有进行合并过的个体。(图一)
2.数据压缩(图二)
图一
图二
tx ty
| |
x ~ y
rootx->rooty = rootx->x + x->y + y->rooty
进一步转化:rootx->rooty = ( relation[x]+ (d-1) +(3-relation[y]) ) %3 = relation[rooty]],(模3是保证偏移量取值始终在[0,2]间)
解释:
1.题目原意 D = 2表示x吃y , 而在我们规定的关系中(D = D-1)1表示 x被y吃,则 (题目意思)x -> tx = tx -> x(自定义规 则) (x吃tx,与tx被x吃一个意思),所以rootx -> x = relation[x]。
2. root x-> rooty = 3-relation[rooty]
#include<iostream>
#include<cstdio>
#include<algorithm>
const int maxn = 50000+5;
using namespace std;
int N,K;
int D,X,Y,ans;
int parent[maxn];
int rela[maxn];
int rx,ry;
void init()
{
for(int i = 1;i <= N;i++){
parent[i] = i;
rela[i] = 0;
}
}
int getroot(int a){
if(parent[a] == a)
return a;
else{
int temp = parent[a]; //递归由最小元素组成思考
parent[a] = getroot(temp);
rela[a] = (rela[a]+rela[temp])%3;
return parent[a];
}
}
int main()
{
scanf("%d %d",&N,&K);
init();
ans = 0;
for(int i = 0;i < K;i++){
scanf("%d%d%d",&D,&X,&Y); //目的:找出有多少个错误输入
if(X>N || Y>N){
ans++;
continue;
}
if(D==2 && X==Y){
ans++;
continue;
}
rx = getroot(X);
ry = getroot(Y);
if(rx != ry){
parent[rx] = ry;
rela[rx] = (3-(rela[X]+(D-1)+(3-rela[Y]))%3);
// parent[ry] = rx;
//rela[ry] = (3+(D-1)+rela[X]-rela[Y])%3;
}
else{
if(D == 1&& rela[X]!=rela[Y]){
ans++;
continue;
}
if(D == 2&&(3-rela[X]+rela[Y])%3 != D-1){
ans++;
continue;
}
}
}
printf("%d\n",ans);
}