HDU2176题意:
m堆石子,两人轮流取.只能在1堆中取.取完者胜.先取者负输出No.先取者胜输出Yes,然后输出怎样取子.
通过 SG定理 我们可以知道每一个数的SG值,等于这个数到达不了的前面数中的最小值。本题题意和尼姆博弈一样,即可以在一堆中任意个石子,所以也就是说每个数都可以到达前面经过的每一个数,所以每一个数的SG值就是它本身。又因为有好多堆石子,所以可以看作多个一堆石子的游戏,我们可以让n代表每一堆石子的数量,那么让所有堆的SG(n)相互异或得到的结果就是答案(这里只是用SG定义来证明了一下尼姆博弈的作法)
HDU2176题解:
如果给出的每一堆石子的总数n相互异或得到0,就证明这是一个必败态
那么做这一道题先判断一个全部异或后得到的是不是0,如果是0直接输出No
不是0的话,就要找方法使得一步操作过后局面变成必败态,变成必败态要是他们所以异或起来是0,而一个数和它自己异或就是0
所以我们可以从所有石子堆中找出来一个数,让它变成除自己外剩下所有值的异或值,这样全部异或起来就是0了
比如(用^代表异或):
(1 2 5) 1^2=3 ,那么我们可以从5中拿走2个石子,这样就变成了必败态
(1 6 9) 1^6=7,那么可以从9中拿走2个石子,这样也变成了必败态
代码:

1 #include<stdio.h>
2 #include<string.h>
3 #include<iostream>
4 #include<algorithm>
5 #include<math.h>
6 #include<stack>
7 #include<math.h>
8 using namespace std;
9 typedef long long ll;
10 const int maxn=1000005;
11 int v[maxn];
12 int main()
13 {
14 int n;
15 while(~scanf("%d",&n))
16 {
17 if(!n) break;
18 int flag=0;
19 for(int i=0;i<n;++i)
20 {
21 scanf("%d",&v[i]);
22 flag^=v[i];
23 }
24 if(!flag)
25 {
26 printf("No\n");
27 continue;
28 }
29 printf("Yes\n");
30 for(int i=0;i<n;++i)
31 {
32 if(v[i]>(flag^v[i]))
33 {
34 printf("%d %d\n",v[i],flag^v[i]);
35 }
36 }
37 }
38 return 0;
39 }
HDU1850题意:
和上一题和基本上一样,就是问你如果能赢,第一步拿石子有多少种方法
题解:
上一道题就是在判断从那一堆中拿石子,所以这一道题只需要稍微改变一下就可以了
代码:

1 #include<stdio.h>
2 #include<string.h>
3 #include<iostream>
4 #include<algorithm>
5 #include<math.h>
6 #include<stack>
7 #include<math.h>
8 using namespace std;
9 typedef long long ll;
10 const int maxn=1000005;
11 int v[maxn];
12 int main()
13 {
14 int n;
15 while(~scanf("%d",&n))
16 {
17 if(!n) break;
18 int flag=0;
19 for(int i=0;i<n;++i)
20 {
21 scanf("%d",&v[i]);
22 flag^=v[i];
23 }
24 int ans=0;
25 for(int i=0;i<n;++i)
26 {
27 if(v[i]>(flag^v[i])) ans++;
28 }
29 printf("%d\n",ans);
30 }
31 return 0;
32 }
