首页 > 其他 > 详细

并查集关系域 POJ-1182 食物链

时间:2020-09-29 20:16:44      阅读:53      评论:0      收藏:0      [点我收藏+]

原题:http://poj.org/problem?id=1182

参考题解:https://blog.csdn.net/niushuai666/article/details/6981689

Language:
食物链
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 119629   Accepted: 36539

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),输出假话的总数。

Input

第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。

Output

只有一个整数,表示假话的数目。

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

 带关系域的并查集

我们用变量re记录对应结点和其父结点的关系。

* 如果re==0 说明当前和fa是同类
* 如果re==1 说明当前被fa吃
* 如果re==2 说明当前吃fa

然后在并查集找最终父结点的过程中更新结点关系。

 

更新结点时结点关系变化的规律?:

我们可以先从三个结点a, b, c开始考虑。

假设c是b的父结点,a是b的子节点。现在要更新a父结点为c。求a结点和c结点的关系。

情况如下图

技术分享图片

 

 在更新的时候,我们只需要按照上表“c变成fa之后的a.re”的内容来更新数据就可以了。其中我们可以发现这个结果为(a.re+b.re)%3。

此段代码为:

1 ll findFather(ll id, ll &totres){//带更新关系域的的找父亲
2     if(nos[id].fa==id) return id;
3     ll fa=findFather(nos[id].fa, totres);
4     totres+=nos[id].re;//累加当前结点以及当前结点之前的re
5     nos[id].re=totres%3;//更新关系域
6     nos[id].fa=fa;//更新父结点
7     return fa;
8 }

 

 

插入结点时结点关系变化的规律?

假设题目现在给出x和y一对关系式,x和y的父结点分别为fax和fay。插入结点时的操作为让fay的父结点变成fax。求fay和fax之间的关系。

情况如下图

技术分享图片

 

按这个表进行插入新结点的re赋值操作。

此段代码为:

 1         nos[fay].fa=fax;
 2         if(1==d){//x和y是同类
 3             if(nos[x].re==0){
 4                 if(nos[y].re==0) nos[fay].re=0;
 5                 else if(nos[y].re==1) nos[fay].re=2;
 6                 else if(nos[y].re==2) nos[fay].re=1;
 7             }
 8             else if(nos[x].re==1){
 9                 if(nos[y].re==0) nos[fay].re=1;
10                 else if(nos[y].re==1) nos[fay].re=0;
11                 else if(nos[y].re==2) nos[fay].re=2;
12             }
13             else if(nos[x].re==2){
14                 if(nos[y].re==0) nos[fay].re=2;
15                 else if(nos[y].re==1) nos[fay].re=1;
16                 else if(nos[y].re==2) nos[fay].re=0;
17             }
18         }
19         else if(2==d){//x吃y
20             if(nos[x].re==0){
21                 if(nos[y].re==0) nos[fay].re=1;
22                 else if(nos[y].re==1) nos[fay].re=0;
23                 else if(nos[y].re==2) nos[fay].re=2;
24             }
25             else if(nos[x].re==1){
26                 if(nos[y].re==0) nos[fay].re=2;
27                 else if(nos[y].re==1) nos[fay].re=1;
28                 else if(nos[y].re==2) nos[fay].re=0;
29             }
30             else if(nos[x].re==2){
31                 if(nos[y].re==0) nos[fay].re=0;
32                 else if(nos[y].re==1) nos[fay].re=2;
33                 else if(nos[y].re==2) nos[fay].re=1;
34             }
35         }

 

如何判断题目给出的x和y关系与之前的内容矛盾?

如果x和y的父结点fax和fay不同,那么说明在之前x和y之间没有任何关系。因此题目给出任何x和y的关系都是不矛盾的。所以我们只考虑x和y的父结点相同的情况。

如果题目给出的d==1,即x和y为同类, 因为fax==fay,那么x和y必然re相同。

如果题目给出的d==2,即x吃y,那么x和y和祖先lca有且仅有三种情况

* 情况1:lca->x->y->lca  那么x.re=1 y.re=2
* 情况2:->x(lca)->y->   那么x.re=0 y.re=1
* 情况3:->x->y(lca)->   那么x.re=2 y.re=0

只要满足其中任意一种情况,那么就不矛盾。

此段代码为:

 1         ll fax=findFather(x, totx);
 2         ll fay=findFather(y, toty);
 3         if(fax==fay){//只有当lca相同时才有可能有冲突 并且不需要更新关系 因为关系已知
 4             if(1==d){//同类re应该相同
 5                 if(nos[x].re!=nos[y].re)
 6                     res++;
 7             }
 8             else if(2==d){//x吃y
 9                 /*
10                  * 情况1:lca->x->y->lca
11                  * 情况2:->x(lca)->y->
12                  * 情况3:->x->y(lca)->
13                  */
14                 bool con1=false, con2=false, con3=false;
15                 if(nos[x].re==1&&nos[y].re==2) con1=true;
16                 if(nos[x].re==0&&nos[y].re==1) con2=true;
17                 if(nos[x].re==2&&nos[y].re==0) con3=true;
18                 if ((!con1) && (!con2) && (!con3)) {
19                     res++;
20                 }
21             }
22             continue;;
23         }

 

 

全部代码:

  1 #include "iostream"
  2 #include "stdio.h"
  3 using namespace std;
  4 typedef long long ll;
  5 const ll mx=5e4+10;
  6 ll N, K;
  7 
  8 typedef struct NODE{
  9     ll re;//关系域
 10     /*
 11      * 如果re==0 说明当前和fa是同类
 12      * 如果re==1 说明当前被fa吃
 13      * 如果re==2 说明当前吃fa
 14      */
 15     ll fa;//父亲
 16 }NODE;
 17 NODE nos[mx];
 18 
 19 ll findFather(ll id, ll &totres){//带更新关系域的的找父亲
 20     if(nos[id].fa==id) return id;
 21     ll fa=findFather(nos[id].fa, totres);
 22     totres+=nos[id].re;//累加当前结点以及当前结点之前的re
 23     nos[id].re=totres%3;//更新关系域
 24     nos[id].fa=fa;//更新父结点
 25     return fa;
 26 }
 27 void init(){
 28     for(ll i=1;i<=N;i++){
 29         nos[i].re=0;
 30         nos[i].fa=i;
 31     }
 32 }
 33 int main() {
 34     ll res=0;
 35     scanf("%lld %lld", &N, &K);
 36     init();
 37     for(ll i=1;i<=K;i++){
 38         ll d, x, y, totx=0, toty=0;
 39         scanf("%lld %lld %lld", &d, &x, &y);
 40         if(x>N||y>N){//当前的话中X或Y比N大,就是假话;
 41             res++;
 42             continue;;
 43         }
 44         if(x==y&&2==d){//当前的话表示X吃X,就是假话。
 45             res++;
 46             continue;
 47         }
 48         ll fax=findFather(x, totx);
 49         ll fay=findFather(y, toty);
 50         if(fax==fay){//只有当lca相同时才有可能有冲突 并且不需要更新关系 因为关系已知
 51             if(1==d){//同类re应该相同
 52                 if(nos[x].re!=nos[y].re)
 53                     res++;
 54             }
 55             else if(2==d){//x吃y
 56                 /*
 57                  * 情况1:lca->x->y->lca
 58                  * 情况2:->x(lca)->y->
 59                  * 情况3:->x->y(lca)->
 60                  */
 61                 bool con1=false, con2=false, con3=false;
 62                 if(nos[x].re==1&&nos[y].re==2) con1=true;
 63                 if(nos[x].re==0&&nos[y].re==1) con2=true;
 64                 if(nos[x].re==2&&nos[y].re==0) con3=true;
 65                 if ((!con1) && (!con2) && (!con3)) {
 66                     res++;
 67                 }
 68             }
 69             continue;;
 70         }
 71         //lca不同时 加入并查集
 72         nos[fay].fa=fax;
 73         if(1==d){//x和y是同类
 74             if(nos[x].re==0){
 75                 if(nos[y].re==0) nos[fay].re=0;
 76                 else if(nos[y].re==1) nos[fay].re=2;
 77                 else if(nos[y].re==2) nos[fay].re=1;
 78             }
 79             else if(nos[x].re==1){
 80                 if(nos[y].re==0) nos[fay].re=1;
 81                 else if(nos[y].re==1) nos[fay].re=0;
 82                 else if(nos[y].re==2) nos[fay].re=2;
 83             }
 84             else if(nos[x].re==2){
 85                 if(nos[y].re==0) nos[fay].re=2;
 86                 else if(nos[y].re==1) nos[fay].re=1;
 87                 else if(nos[y].re==2) nos[fay].re=0;
 88             }
 89         }
 90         else if(2==d){//x吃y
 91             if(nos[x].re==0){
 92                 if(nos[y].re==0) nos[fay].re=1;
 93                 else if(nos[y].re==1) nos[fay].re=0;
 94                 else if(nos[y].re==2) nos[fay].re=2;
 95             }
 96             else if(nos[x].re==1){
 97                 if(nos[y].re==0) nos[fay].re=2;
 98                 else if(nos[y].re==1) nos[fay].re=1;
 99                 else if(nos[y].re==2) nos[fay].re=0;
100             }
101             else if(nos[x].re==2){
102                 if(nos[y].re==0) nos[fay].re=0;
103                 else if(nos[y].re==1) nos[fay].re=2;
104                 else if(nos[y].re==2) nos[fay].re=1;
105             }
106         }
107     }
108     cout<<res<<endl;
109     return 0;
110 }

 

并查集关系域 POJ-1182 食物链

原文:https://www.cnblogs.com/sloth612/p/13721192.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!