1182 食物链

来源:互联网 发布:房地产数据公司 编辑:程序博客网 时间:2024/05/18 15:08

//1182 食物链 利用向量偏移的并查集
//
//每个集合都有三类动物,用rank表示,0——同类;1——食物;2——天敌,初始时都以自己为根节点,且rank为0
//只需对每一句话,判断x,y是否同一集合内,如果是就判断语句真假,否则就合并集合
//根据公式知道x-y=d-1,主要想出几个推导公式:
//1:  rank[x]=(rank[x]+rank[temp])%3    
//     这个最好设一两个例子验证一下,由于每次都是两集合的根合并,且只有根更改rank,所以每个x的rank值只会与
//     它的father有关(只有一次father更改,就是跟合并时),然后结合向量偏移得到。
//  比如a-b = 1 b-c = 1 那么a-c等于2
//三个公式都与向量偏移计算得来,看下面的详解
//附1:
//向量偏移解说
/*
> 什么叫做向量的思维模式?
> Orz Orz
我的理解是,对于集合里的任意两个元素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 = (delta[a]+d-1+3-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<stdio.h>#include<string.h>#include<iostream>#include<algorithm>using namespace std;#define MAXN 50010int father[MAXN];int rank[MAXN];int ans=0;int n,m;void init(int x){int i;for(i=1;i<=n;i++){father[i]=i;rank[i]=0;}}int find(int x){int t;if(father[x]==x)return x;t=find(father[x]);rank[x]=(rank[x]+rank[father[x]])%3;father[x]=t;return t;}void union_set(int x,int y,int d){if(x>n||y>n){ans++;return ;}int a=find(x);int b=find(y);if(a==b){if((rank[x]-rank[y]+3)%3!=d){ans++;return ;}}father[a]=b;rank[a]=(rank[y]+d+3-rank[x])%3;}int main(){int x,y,d;int i;scanf("%d%d",&n,&m);init(n);while(m--){scanf("%d%d%d",&d,&x,&y);union_set(x,y,d-1);}printf("%d\n",ans);return 0;}


原创粉丝点击