Solution 欧几里得的游戏
题目大意:有两个数(m,n),你可以将较大的数减去较小的数的正整数倍,先得到(0)的人胜。问给定(m,n)是否有先手必胜方案
博弈论
分析:首先可以转化成一个有向图游戏
我们定义(SG(m,n)),不妨钦定(mgeq n)那么根据题意,显然(SG(m,n)=mex{SG(m-n,n),SG(m-2n,n)dots SG(n,m\%n)})(显然(m\%n < n))
暴力计算(SG)函数复杂度过高无法承受,我们可以考虑优化,观察式子发现(SG(m-n,n),SG(m-2n,n)dots)如果一直递归下去,会得到(SG(n,m\%n)),因此我们用类似欧几里得算法的方法递归计算(题目名称给提示啊)
然后注意一点,若(lfloor frac{m}{n} floor > 1),那么(SG(m,n) > 0),考虑证明,如果(lfloor frac{m}{n} floor > 1),那么这个人可以取(nlfloor frac{m}{n} floor)转移到(SG(n,m\%n)),也可以取(n(lfloor frac{m}{n} floor-1))转移到(SG(m \% n + n,n)),让另一个人再取(n),这两种情况奇偶性不同,因此总有一种状态是必败状态,所以当前状态是必胜状态
另外情况
(SG(m,0)=0)
(SG(m,n)=mex(SG(n,m\%n))=!SG(n,m\%n))(如果不是(0)就取(1)的话)
#include <cstdio>
using namespace std;
int t,n,m;
inline int min(int a,int b){return a < b ? a : b;}
inline int max(int a,int b){return a > b ? a : b;}
inline bool solve(int a,int b){
if(!b)return false;
if(a / b > 1)return true;
return !solve(b,a % b);
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d %d",&n,&m);
puts(solve(max(n,m),min(n,m)) ? "Stan wins" : "Ollie wins");
}
return 0;
}