【并查集】食物链

【并查集】食物链

题目:
动物王国中有三类动物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),输出假话的总数。
输入:
第一行是两个整数N和K,以一个空格分隔。

以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。

 若D=1,则表示X和Y是同类。

 若D=2,则表示X吃Y。
输出:
只有一个整数,表示假话的数目。
数据范围:
如题。


一道让我整整想了两天的题,太难了...
我一开始想到每次先把一只陌生动物默认为A种,每种一个集合
后来打着打着觉得不对劲,发现这样答案是错的
于是开始冥思苦想
我发现:其实不用知道他是什么种的动物,只要知道他和其他的动物的关系就可以了
经过不断的尝试,终于打出了:30
相信自己,坚持不懈!
又看了看题解,不停的改进
功夫不负有心人,终于AC了!

讲一讲:
其实判断话的对错最难的地方就是在第一个条件
我们分情况处理
当他说两只动物相同时,我们先看两只动物是否在同一棵树上(即比较根节点,这里需要用并查集)
//在压缩路径的同时,将p数组更新(加上前一个)
p数组用来存储的是这个点当前与他的父亲的关系
0:相同种类
1:被父亲吃
2:吃父亲
如果父亲不相同,那么这句话肯定是对的
然后将y的根结点指向x的根结点
如果父亲相同:
判断他们与父亲的关系的差是否满足描述
中间有一些式子会比较难,大家认真推一推,总能推出来的


标程:(请勿copy)

var     father:array[0..50000,1..2]of longint;
        x,y,n,k,i,j,l,s,z,m,ans,mode,x1,y1,x2,y2:longint;
function find(t:longint):longint;
var     x:longint;
begin
        z:=(z+father[t,2])mod 3;
        if father[t,1]=t then exit(t)
        else exit(find(father[t,1]));
end;
begin
        readln(n,k);
        for i:=1 to n do
                father[i,1]:=i;
        for i:=1 to k do
        begin
                readln(mode,x,y);
                if(x>n)or(y>n)then
                        continue;
                if(mode=2)and(x=y)then
                        continue;
                case mode of
                1:
                        begin
                                z:=0;
                                x1:=find(x);
                                father[x,1]:=x1;
                                father[x,2]:=z;
                                z:=0;
                                y1:=find(y);
                                father[y,1]:=y1;
                                father[y,2]:=z;
                                if(x1=y1)and(father[y,2]=father[x,2])then
                                        inc(ans);
                                if x1<>y1 then
                                begin
                                        inc(ans);
                                        father[y1,1]:=x1;
                                        father[y1,2]:=(father[x,2]-father[y,2]+3)mod 3;
                                end;
                        end;
                2:
                        begin
                                z:=0;
                                x1:=find(x);
                                father[x,1]:=x1;
                                father[x,2]:=z;
                                z:=0;
                                y1:=find(y);
                                father[y,1]:=y1;
                                father[y,2]:=z;
                                if(x1=y1)and((father[x,2]+1)mod 3=father[y,2])then
                                        inc(ans);
                                if x1<>y1 then
                                begin
                                        inc(ans);
                                        father[y1,1]:=x1;
                                        father[y1,2]:=(father[x,2]-father[y,2]+4)mod 3;
                                end;
                        end;
                end;
        end;
        writeln(k-ans);
end.


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值