题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3389
题意:1-N带编号的盒子,当编号满足A>B && A非空 && (A + B) % 3 == 0 && (A + B) % 2 == 1则可以从A中取任意石头到B中,谁不能取了谁就输。
由已知条件可得到 (A+B) % 6 = 3 ,即((A % 6)+(B % 6))% 6 = 3,于是可以知道 编号%6 == 0的盒子可以转移到 编号%6 == 3的盒子里, 编号%6==1的盒子可以转移到 编号%6==2的盒子...,也就是下面这样:
0 -> 3 -> 0 -> 3
2 -> 1 -> 2 -> 1
5 -> 4 -> 5 -> 4
可以发现(怎么发现的。。。)最后的1, 3, 4盒子都是不能继续转移的盒子。
这三个转移都是互不影响的,所以可分三个子游戏:
1.余0的箱子中的卡只能移到余3里,余3的箱子只能移到余0里。最终箱子编号为3;
2.余2的箱子中的卡只能移到余1里,余1的箱子只能移到余2里。最终箱子编号为1;
3.余4的箱子中的卡只能移到余5里,余5的箱子只能移到余4里。最终箱子编号为4;
对于1子游戏,若一个人从余3移一定数目的卡到余0的a箱里,那另一个也必定可以从a箱中移出相同数目的卡。所以
我们只用考虑从余0中移出的情况(因为余0中移到3号箱中,另一个就不能从3号箱子移出卡,不能与上述判断等同),
而从余0中移到余3中可以看做从余0中删除,因为余数3的可以根据上述方式最终移动3中,而先后手顺序不变。
这就变成了取石子游戏了,sg[i]=i。 其余的2,3同理。最终求得的sg函数值,异或便是结果了。
另一种解释:对于第一个转移公式我们可以发现如果当前决策者处于不利形势,他想要挽回形式而移动x%6==3的盒子到x%6==0的盒子是完全没用的,因为对手总是可以继续把你转移的盒子再转移回x%6==3的盒子里使得你还是处于同样的劣势,所以在这里影响结果的只有x%6==0的盒子,最终是要把x%6==0的盒子里的移动到3这个盒子里,问题似乎就转换成了普通的NIM取石子游戏。。。sg值就是和NIM游戏一样为盒子里的卡片数!对于其他两个转移公式同样如此!
参考博客:http://blog.csdn.net/a601025382s/article/details/11910303
http://www.2cto.com/kf/201311/255113.html
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
int main() {
int t;
scanf("%d", &t);
int kase = 1;
while(t--) {
int n;
scanf("%d", &n);
int a[10010];
int i, ans = 0;
for(i = 1; i <= n; i++) {
scanf("%d", a + i);
if(i % 6 == 0 || i % 6 == 2 || i % 6 == 5) {
ans ^= a[i];
}
}
if(ans) printf("Case %d: Alice\n", kase++);
else printf("Case %d: Bob\n", kase++);
}
return 0;
}