喵哈哈村的代码传说 第五章 找规律
发布时间: 2017年3月11日 16:04 最后更新: 2017年3月11日 16:04 时间限制: 1000ms 内存限制: 128M
桌面上有n组牌,每组牌有a[i]个扑克,两个玩家轮流拿扑克,谁拿到最后一张扑克,谁就胜利了。
但是这个规矩有点奇怪:他们可以选择一堆拿任意多的扑克牌,或者选择使得某一堆扑克牌变成三堆任意大小的扑克牌。
请问如果两个人都非常机智的话,谁能获胜呢?
第一行包括数据组数T,表示测试数据组数。
接下来T组测试数据:
第一行一个n,表示有n组牌。
第二行n个数字a[i],表示每组牌的数量。
保证n<=1000000,a[i]<=1e9
第一个人胜利输出First player wins.
第二个人胜利输出Second player wins.
复制
2 2 4 4 3 1 2 4
Second player wins. First player wins.
一看到题目,找规律?
这不简单? 然后就开始找规律...2的倍数 3的倍数 各种模?
博弈论--sg函数
sg函数定义:sg(x)=mex{y|sg(y)} y为x可以到达的子状态,mex为集合中未出现的最小整数
还有一个结论
直接说结论好了。(Bouton's Theorem)对于一个Nim游戏的局面(a1,a2,...,an),它是P-position当且仅当a1^a2^...^an=0,其中^表示异或(xor)运算。
怎么样,是不是很神奇?我看到它的时候也觉得很神奇,完全没有道理的和异或运算扯上了关系。但这个定理的证明却也不复杂,基本上就是按照两种position的证明来的。
所以对于一个由多个子游戏组成的游戏,直接 sg(i1)^sg(i2)^sg(i3)...
枚举一下发现 当sg=k*8+7 时,sg有子状态k*8+7 举个栗子...sg=7 它可以分成小游戏 1 2 4三堆牌, 从这三堆牌里面拿 sg异或和为7,所以此时sg应该为8...
找到这个规律之后...就是简单的取石子游戏了(Nim)
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 typedef long long ll; 5 ll n, m; 6 int main(){ 7 cin>>n; 8 while(n--){ 9 cin>>m; 10 ll a, sg=0; 11 for(int i=0; i<m; i++){ 12 cin>>a; 13 if(a%8==7) a=a+1; 14 else if(a%8==0) a=a-1; 15 sg=sg^a; 16 } 17 if(sg!=0) printf("First player wins. "); 18 else printf("Second player wins. "); 19 } 20 return 0; 21 }