一些真理
所以并查集到底是啥,是一个树?是一个连通分量?其实我们可以认为这是一个向量。为什呢?我们来看这个图。
假设存在关系A-B、B-C,根据这两个关系去构建并查集得到的结论不就是A-C有关系吗?实际上就是两个向量做加法,并查集的路径压缩就是在做这件事,向量路径缩短了,查询的速度自然而然就快起来了。而之前纠结的连通性问题其实就是向量存不存在的问题,换个角度来看,别有一番风味。结合一下实际,看一下一个经典案例。
http://poj.org/problem?id=1182
食物链问题,大意是某处存在三类生物,A能吃B,B能吃C,C能吃A,题目输入食物链的相关内容,要求检查输入食物链是否合理,这里单纯用一个数组描述两个节点rootX和X的关系已经不够,两个生物间存在同类,捕食和被捕食三种关系,这里用一个结构体来表示节点信息。
typedef struct Node {
int preNode;
int relation;
} LineNode;
LineNode faction[50005];
现在假设存在这样的关系,rootX吃X,rootY吃Y,不难看出这里二者构成了两个连通分量,如果此处指定rootX,rootY不相同,且X吃Y或者X和Y是同类,那么因为X和Y存在关系,那么这里要合并两个连通分量。
那么最后的结果应该是faction[AneY].preNode = AneX; 但是只更新上下级是远远不够的,还应该更新关系域,关系域就涉及到向量的关系了,
在代码上写出来就是.
faction[AneY].relation = (faction[X].relation+(opt-1)-faction[Y].relation)%3;
更多内容可以参考这边文章,讲的很好,直接讲透了。
https://blog.csdn.net/niushuai666/article/details/6981689
下面是我的代码。
#include<cstdio>
typedef struct Node {
int preNode;
int relation;
} LineNode;
LineNode faction[50005];
int findAncestor(int x);
using namespace std;
int main(void) {
int n, k;
scanf("%d %d", &n, &k);
int sum = 0;
int level = 1;
for(int i = 1; i <= n; i++) {
faction[i].preNode = i;
faction[i].relation = 0;
}
while(k--) {
int opt, num1, num2;
scanf("%d %d %d", &opt, &num1, &num2);
if(num1 > n || num2 > n) {
sum++;
continue;
}
int AneX = findAncestor(num1);
int AneY = findAncestor(num2);
if(AneX != AneY) {
faction[AneY].preNode = AneX;
faction[AneY].relation = (faction[num1].relation+(opt-1)-faction[num2].relation+3)%3;
}else {
if(opt==1 && faction[num1].relation!=faction[num2].relation) {
sum++;
}
if(opt==2 && (faction[num2].relation-faction[num1].relation+3)%3!=1) {
sum++;
}
}
}
printf("%d\n", sum);
return 0;
}
int findAncestor(int x) {
if(faction[x].preNode == x) {
return x;
}
int pre = faction[x].preNode;
faction[x].preNode = findAncestor(pre);
faction[x].relation = (faction[pre].relation+faction[x].relation)%3;
return faction[x].preNode;
}
|