问题 A: 食物链
时间限制: 1 Sec 内存限制: 128 MB题目描述
动物王国中有三类动物 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。
输出
只有一个整数,表示假话的数目。
样例输入
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
样例输出
3
提示
100 7
1 101 1 假话
2 1 2 真话
2 2 3 真话
2 3 3 假话
1 1 3 假话
2 3 1 真话
1 5 5 真话
【分析】
1.如果a吃b,b吃c,c吃d,那么a和d为同类;
2.如果a吃b,c也吃b,那么a和c也为同类;
显而易见,这道题目就可以用“并查集”来完成。
如果x吃y则有一条x指向y边权为1的边,如果x被y吃则有一条x指向y边权为2的边;
处理:如果x y在一个集合则描述判断是否合法,如果x y不在一个集合则合并两个集合;
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 int p[50005],f[50005]; //p mean the father of i; 7 //f mean the length of i to its father; 8 int father(int x) //find its father 9 { 10 if (x!=p[x]) 11 { 12 int temp=p[x]; 13 p[x]=father(p[x]); 14 f[x]=(f[x]+f[temp])%3; 15 } 16 return p[x]; 17 } 18 int main() 19 { 20 int n,k,d,x,y,count=0; 21 scanf("%d%d",&n,&k); 22 for (int i=1;i<=n;i++) 23 { 24 p[i]=i; f[i]=0; 25 } //firstly 26 for (int i=1;i<=k;i++) 27 { 28 scanf("%d%d%d",&d,&x,&y); 29 if ((d==2&&x==y)||(x>n||y>n)) 30 { 31 count++; 32 continue; 33 } 34 if (d==1) 35 { 36 if (father(x)==father(y)) 37 { 38 if (f[x]!=f[y]) count++; 39 } 40 else 41 { 42 f[p[x]]=(f[y]-f[x]+3)%3; 43 p[p[x]]=p[y]; 44 } 45 } 46 if (d==2) 47 { 48 if (father(x)==father(y)) 49 { 50 if (f[x]!=((f[y]+1)%3)) count++; 51 } 52 else 53 { 54 f[p[x]]=(f[y]-f[x]+4)%3; 55 p[p[x]]=p[y]; 56 } 57 } 58 } 59 printf("%d",count); 60 61 }
一段时间后再回来研究这道题,并且要学习一下带权并查集。
(之前还有一个并查集专题,也可以再回顾一下啊)
带权并查集
带权值的并查集只不过是在并查集中加入了一个value[ ]数组
value[ ]可以记录很多种东西,不一定是类似距离这种东西,也可以是相对于根节点的状态
加入了权值,函数应该有一些改变
function find(x:longint):longint; var tmp:longint; begin if father[x]=x then exit(x); tmp:=father[x]; father[x]:=find(father[x]); //下面一句可以随意改 value[x]:=value[tmp]+1; exit(father[x]); end;
-知识点完-
接下来上食物链题解(简单粗暴的题解)
var n,m,i,ans:longint; father,size:array[0..50000]of longint; function find(x:longint):longint; var t:longint; begin if father[x]=x then exit(x) else begin t:=father[x]; father[x]:=find(father[x]); //路径压缩 size[x]:=(size[x]+size[t]) mod 3; //根据找规律可得,各种情况两两结合可得size exit(father[x]); end; end; function judge:boolean; var kind,x,y,fx,fy:longint; begin //以下的各种判定可用向量的规律判断得 readln(kind,x,y); if (x>n)or(y>n) then exit(false); if (kind=2)and(x=y) then exit(false); fx:=find(x); fy:=find(y); if fx=fy then begin if (size[y]-size[x]+3) mod 3<>(kind-1) then exit(false) else exit(true); end else begin father[fy]:=fx; size[fy]:=(size[x]-size[y]+kind-1+3) mod 3; exit(true); end; end; begin readln(n,m); for i:=1 to n do begin father[i]:=i; size[i]:=0; end; for i:=1 to m do if judge=false then ans:=ans+1; writeln(ans); end.