题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2176
题目:
Problem Description
m堆石子,两人轮流取.只能在1堆中取.取完者胜.先取者负输出No.先取者胜输出Yes,然后输出怎样取子.例如5堆 5,7,8,9,10先取者胜,先取者第1次取时可以从有8个的那一堆取走7个剩下1个,也可以从有9个的中那一堆取走9个剩下0个,也可以从有10个的中那一堆取走7个剩下3个.
Input
输入有多组.每组第1行是m,m<=200000. 后面m个非零正整数.m=0退出.
Output
先取者负输出No.先取者胜输出Yes,然后输出先取者第1次取子的所有方法.如果从有a个石子的堆中取若干个后剩下b个后会胜就输出a b.参看Sample Output.
Sample Input
2
45 45
3
3 6 9
5
5 7 8 9 10
0
Sample Output
No
Yes
9 5
Yes
8 1
9 0
10 3
思路:Nim博弈裸题,将每堆的石子数异或起来,如果为0,那么先手必败,否则先手必胜。不过,这题还要求我们求出所有的第一步取石子的方案,我们知道a^b^a = a,而Nim博弈的原理是如果先手不是开场就已经确定必败,那么先手就可以通过拿石子来将局势转换为后手必败(即异或和为0),要做到这一点只需将某一堆取走其他所有石子数异或到的值就行,因为a^a=0。
代码实现如下:
1 #include <cstdio> 2 #include <iostream> 3 using namespace std; 4 5 const int maxn = 2e5 + 7; 6 int n, ans; 7 int a[maxn]; 8 9 int main() { 10 while(cin >>n && n) { 11 ans = 0; 12 for(int i = 0; i < n; i++) { 13 cin >>a[i]; 14 ans ^= a[i]; 15 } 16 if(ans == 0) puts("No"); 17 else { 18 puts("Yes"); 19 for(int i = 0; i < n; i++) { 20 int k = ans ^ a[i]; 21 if(k < a[i]) { 22 printf("%d %d\n", a[i], k); 23 } 24 } 25 } 26 } 27 return 0; 28 }