【并查集】食物链
题目:
动物王国中有三类动物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.