剪刀石头布
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1246 Accepted Submission(s): 375
Problem Description
现有M个人一起玩剪刀石头布,以1-M编号,每人出一种,出过不再改变,但是我们并不知道它到底是哪一种。 (其中石头赢剪刀,剪刀赢布,布赢石头,一样则平) 裁判用两种说法对这M个人所构成的输赢关系进行描述: 一:"1 A B",表示第A个人和第B个人出的一样。 二:"2 A B",表示第A个人赢第B个人。 裁判对M个人,用以上两种说法,连说N句话,其中有真的、也有假的。 一句话出现以下情况,就是假话,否则就是真话。 1) 该句话与之前的某些真话冲突; 2) 该句话中A或B比M大; 3) 该句话表示A赢A。 请根据给定的M和N,输出假话数。 其中(1 <= M <= 10,000),(0 <= N <= 10,000)
Input
第1行是一个自然数K,代表有K组数据。 每组数据以一个空行分隔,其中每组数据的第1行是两个自然数M、N,以空格分开。 每组数据的第2行至N+1行,每行是三个自然数X,A,B,三个数之间用空格分开,X(1或2)表示说法的种类。
Output
每组数据对应一行,每行有一个整数,代表假话数。
Sample Input
3 43 11 1 4 3 2 3 3 1 4 1 1 4 4 2 3 3 1 2 2 2 1 4 1 1 1 2 1 4 2 3 4 2 3 2 66 9 2 3 1 2 4 4 2 1 2 2 4 3 2 4 2 2 2 3 1 3 2 1 2 1 1 1 1 6 7 2 3 7 2 1 2 2 4 4 1 2 1 1 3 2 1 2 3 2 1 3
Sample Output
5 4 3这道题和食物链那到题几乎就是一道题。。所以不多说了,直接贴代码。。#include<cstdio> const int maxn = 50000+10; int p[maxn]; //存父节点 int r[maxn];//存与父节点的关系 0 同一类,1被父节点吃,2吃父节点 void set(int n) //初始化 { for(int x = 1; x <= n; x++) { p[x] = x; //开始自己是自己的父亲节点 r[x] = 0;//开始自己就是自己的父亲,每一个点均独立 } } int find(int x) //找父亲节点 { if(x == p[x]) return x; int t = p[x]; p[x] = find(p[x]); r[x] = (r[x]+r[t])%3; //回溯由子节点与父节点的关系和父节点与根节点的关系找子节点与根节点的关系 return p[x]; } void Union(int x, int y, int d) { int fx = find(x); int fy = find(y); p[fy] = fx; //合并树 注意:被 x 吃,所以以 x 的根为父 r[fy] = (r[x]-r[y]+3+(d-1))%3; //对应更新与父节点的关系 } int main() { int n, m,k; scanf("%d",&k); while(k--) { scanf("%d%d", &n, &m); set(n); int ans = 0; int d, x, y; while(m--) { scanf("%d%d%d", &d, &x, &y); if(x > n || y > n || (d == 2 && x == y)) ans++; //如果节点编号大于最大编号,或者自己吃自己,说谎 else if(find(x) == find(y)) //如果原来有关系,也就是在同一棵树中,那么直接判断是否说谎 { if(d == 1 && r[x] != r[y]) ans++; //如果 x 和 y 不属于同一类 if(d == 2 && (r[x]+1)%3 != r[y]) ans++; // 如果 x 没有吃 y (注意要对应Uinon(x, y)的情况,否则一路WA到死啊!!!) } else Union(x, y, d); //如果开始没有关系,则建立关系 } printf("%d\n", ans); } return 0; }