POJ-1182-食物链- 经典并查集应用

来源:互联网 发布:阿里云访问美国ip慢 编辑:程序博客网 时间:2024/05/29 11:09

POJ - 1182

食物链
Time Limit: 1000MS Memory Limit: 10000KB 64bit IO Format: %I64d & %I64u

Submit Status

Description

动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。 
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。 
有人用两种说法对这N个动物所构成的食物链关系进行描述: 
第一种说法是"1 X Y",表示X和Y是同类。 
第二种说法是"2 X Y",表示X吃Y。 
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。 
1) 当前的话与前面的某些真的话冲突,就是假话; 
2) 当前的话中X或Y比N大,就是假话; 
3) 当前的话表示X吃X,就是假话。 
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。 


用到 并查集的fa[]数组,指的是 当前点x的祖先,

而用一个path【】数组,表示 当前点x与祖先的距离, 如果距离%3==0,与祖先为同类,距离%3==1表示 祖先吃x,距离%3==2表示x吃祖先,

之所以模三 是因为 关系就三种,且 刚好是 连续、构成一个环的关系。  因为关系只有三种  所以 大于等于三的距离,与 其自身对三的取模值 是等价的

【所以一个fa根节点的集合内,他们之间所有节点的联系 是 x 与祖先的 关系】

首先肯定判断是否有 自己吃自己的情况,其次 x、y大于n的情况. 

并且 如果 给出的x与y 根本不在一个集合内,那么无论怎么说,都是合法的(因为是第一次出现)

如果d==1,表示 认为x与y是同类

我们找到他们的根,fx,fy。

如果fx==fy;   那么他们是同一祖先,判断他们是否同类==》 判断他们与祖先的关系是否同样==》 他们与祖先的距离是否一样,即path[x]==path[y];

如果fx!=fy;    那么他们不是同祖先, 新给的关系肯定是合法的,我们只需要维护,也就是 把两个集合合并 (本代码以fy为新根), 并更新fx集合中所有点到新根fy的距离,更新的过程如下:

首先 原来 y-->fy的距离为f[y]  , x---->fx的距离为f[x],

合并之后的关系是  x---->fx------>fy<--------y;

 因为x与y同类,所以distance(x,y)=0, 

所以他们的【距离关系】可以近似看作这样的情况  y(x)----->fx---->fy;

所以得出  path[fx]=path[y]-path[x]; 前者指y到fy的距离,后者指,x到fx的距离;

如果d==2,表示x吃y

先找根

如果fx==y;   那么他们是同一祖先,判断是否满足x吃y ==》 判断他们与祖先关系只差是否为2 ,即((path[x]-path[y]-2)+3)%3==0 ;取模是怕出现负数  至于为什么用 差2 而不是差1 ,与我们每次选fy为根节点有关,见代码注释;

如果fx!=fy,则需要 维护,也就是合并集合, 并更新fx集合中所有点到新根fy的距离,更新的过程同上: 

首先 原来 y-->fy的距离为f[y]  , x---->fx的距离为f[x],

合并之后的关系是  x---->fx------>fy<--------y;

 因为x吃y,所以distance(x,y)=2,  //ps:如果一直默认以fx为新根,dis会是1,想想为什么,和上面的判断是同一个道理

所以他们的【距离关系】可以近似看作这样的情况  x---->y----->fx---->fy;

所以得出  path[fx]=path[y]-path[x]+2; 前者指y到fy的距离,后者指,x到fx的距离;


所有path的计算都要注意如果得到负数或者大于等于3的结果要取模,本代码的实现放在里find函数中


#include <cstdio>#include <cmath>#include <cstring>#include <string>#include <algorithm>#include <iostream>#include <queue>#include <map>#include <set>#include <vector>using namespace std;int n;int path[50005];int fa[50005];int find(int x){if (fa[x]==x){path[x]=(path[x]+3)%3;return fa[x];}else  { int fx=find(fa[x]);path[x]=(path[fa[x]]+path[x]+3)%3;// 更新为:自己到原先fa[x]的路径 + 新的fa[x]到根节点的路径return fa[x]=fx;  }}int main(){ int n,k;cin>>n>>k;int op,x,y;int i ;for (i=1;i<=n;i++)fa[i]=i,path[i]=0;int cun=0;for (i=1;i<=k;i++){scanf("%d%d%d",&op,&x,&y);if (x>n||y>n)  {cun++;continue;}if (x==y&&op==2)  {cun++;continue;}int fx=find(x);int fy=find(y);//找到各自的根if (op==1)//同类判断{if (fx==fy)//如果同根{if (path[x]!=path[y]) //那么他们与祖先的距离应该相同,cun++;continue;}else if (fx!=fy)//如果不同类则合并{fa[fx]=fy;//由fx指向fy;path[fx]=path[y]-path[x];  //由向量关系得 path[fx]=path[y]+dis(x-y)-path[x],其中dis(x-y)=0;  //注意可能为负数,因此在find函数中加了+3%3操作,也可直接在此+3%3}}else{//因为当x吃y,且要合并他们时,我们默认把fy作为新根,所以描述x吃y的关系用的是 path[x]=path[y]+2;//如果用fx为根,那么应该用path[x]=path[y]+1;if (fx==fy)//由于上一行的关系,所以此处判断是否符合x吃y的关系式 是path[x]-path[y]!=2 而不是差1{if ((path[x]-path[y]+3)%3!=2)  //这比较必定要+3%3cun++;}else{fa[fx]=fy;path[fx]=path[y]+2-path[x];   //注意可能为负数,因此在find函数中加了+3%3操作,也可直接在此+3%3}  }}printf("%d\n",cun);return 0;}








0 0
原创粉丝点击