http://poj.org/problem?id=1182这道食物链题目是并查集的变型。非常久曾经做的一次是水过的,这次细致地研究了这“食物链”,无非就是运用向量偏移。从曾经节点与节点转化成向量与向量的关系。我们能够把矛盾的产生得益于向量偏移时的结果。
直接引出向量偏移的运用。
以下是POJ一位大牛这样理解的,本人稍有改动。
对于集合里的随意两个元素a,b而言,它们之间必然存在着某种联系,由于并查集中的元素均是有联系的。否则也不会被合并到当前集合中。
那么我们就把这2个元素之间的关系量转化为一个偏移量。以食物链的关系而言。最好还是如果
a->b 偏移量0时 a和b同类
a->b 偏移量1时 a吃b
a->b 偏移量2时 a被b吃,也就是b吃a
有了这些基础,我们就能够在并查集中完毕随意两个元素之间的关系转换了。最好还是继续如果,a的当前集合根节点aa。b的当前集合根节点bb,a->b的偏移值为d-1(题中给出的询问已知条件)
(1)假设aa和bb不同样,那么我们把bb合并到aa上,而且更新delta[bb]值(delta[i]表示i的当前集合根节点到i的偏移量)
此时 aa->bb = aa->a + a->b + b->bb,可能这一步就是所谓向量思维模式吧
上式进一步转化为:aa->bb = (3-delta[a]+d-1+delta[b])%3 = delta[bb]。(模3是保证偏移量取值始终在[0,2]间)
以图示表示为:
(2)假设aa和bb同样,那么我们就验证a->b之间的偏移量是否与题中给出的d-1一致
此时 a->b = a->aa + aa->b = a->aa + bb->b,
上式进一步转化为:a->b = (3+delta[a]-delta[b])%3,若一致则为真。否则为假。
以图示表示为:
一般化总结:
并查集的偏移向量属于并查集的变形,仅仅要适用于集合数目较少,或是固定的并查集类型。
#include<iostream> #include<cstdio> #define maxn 50001 using namespace std; int uset[maxn],rel[maxn]; int find_uset(int x) { if(uset[x]!=x) { int k=uset[x]; //先写 uset[x]=find_uset(uset[x]); //注意上下两处。由于递归调用循序的原因。rel[x]=(rel[k]+rel[x])%3; //后写。 } return uset[x]; } int make_uset(int x,int y,int d) { int ux,uy; if((ux=find_uset(x))==(uy=find_uset(y))) { if((3+rel[x]-rel[y])%3!=(d-1)) return 1; return 0; } else { uset[ux]=uy; rel[ux]=(3-rel[x]+d-1+rel[y])%3; return 0; } } int main() { int n,k; scanf("%d%d",&n,&k); int d,x,y,cnt=0; for(int i=1;i<=n;i++) { uset[i]=i; rel[i]=0; } for(int i=0;i<k;i++) { scanf("%d%d%d",&d,&x,&y); if(x>n||y>n||(d==2&&x==y)) cnt++; else if(make_uset(x,y,d)) cnt++; } printf("%d\n",cnt); return 0; }