洛谷
P1290 欧几里德的游戏
题目链接
思路:
如果是第一个数减一下第二个数然后第二个数减一下第一个数这样反复的话,其实双方都没什么选择,谁先减出0谁就赢了,比如:
0 1
1 1
1 2
3 2
3 5
...
但是如果一个数可以减好几次另一个数,那么面对这个局面的人其实就可以自由选择减完后的状态。具体地说,如果有一对数a b,如果$x=\lfloor a/b\rfloor \ge 2 $,那么我可以选择给对方a-x*b b这一对数,也可以给对方a-(x-1)*b b,对方只能给前者减一下b,这样我就可以面对a-x*b b这一对数了。
对a-x*b b这一对数,肯定要么必胜要么必败,既然我可以选择谁来面对必胜态,谁来面对必败态,那么我就已经赢了。
所以就跑一遍辗转相除法,如果一次辗转中一个数可以减多次另一个数,那么面对这个局面的人就赢了。
对这种对称的规则(可进行的操作不随角色的改变而改变),博弈论其实并不是胜者与败者之间的博弈,因为面临必败态的角色无论怎么选都一定输了。套用一个题解的话:必败态从来没有最佳策略, 博弈也不是双方的博弈, 而是处在必胜态的那方和自己博弈. 而这场博弈, 由于绝顶聪明的前提, 是必胜的, 而我们要做的, 只是找出谁有跟自己博弈的机会
看SG函数的话,很容易看出,必败态相连的一定都是必胜态,必败态父节点这一位置必胜态上的人一定可以转移到必败态上,而必败态上的人无论怎么转移,都一定会走到必胜态上给对方。
code:
#include <iostream>
#include <cstdio>
using namespace std;
int T;
int gcd(int a,int b){
if(b==0)return 0;
if(b>a)swap(a,b);
// cout<<a<<" "<<b<<endl;
return (b<a-b)?1:gcd(b,a%b)^1;
}
int main(){
cin>>T;
while(T--){
int n,m;
cin>>n>>m;
puts((gcd(n,m))?"Stan wins":"Ollie wins");
}
return 0;
}