暑假搞了不少博弈的题,这题其实还是挺好想的,但是晚上没来的及看,,补题的时候各种BUG啊,,真是怀疑人生了,,,
思路:
其实SG就是用来描述局势的,他的定义是最小没出现过的自然数
然后我们把题目给出的游戏分割成好几个小游戏
每个小游戏都有自己的SG值
异或起来就是结果
然后这题:小游戏就是每个质数都对应一个小游戏
就是把所有数都唯一分解
然后就得到了一个个质数的小游戏,根据不同的幂次,可以看成若干石头堆,但是取法是按题意的来,可以认为是如果在一个石头堆里取若干个石头,那么其他不少于这个数量的石头堆都要取这么多石头,这里可以用位运算来压缩状态。
#include<bits/stdc++.h>
using namespace std;
map<int,int> mp;
map<int,int> game;
set<int> vis;
int get_sg(int k)
{
// cout<<"k="<<k<<endl;
if(k==0) return 0;
if(vis.count(k))
return mp[k];
vis.insert(k);
set<int> num;
for(int i=0;(1<<i)<=k;i++)
{
int z=(k>>(i+1))|(k%(1<<i));
if(z!=k) num.insert(get_sg(z));
}
int ret=0;
while(num.count(ret)) ret++;
return mp[k]=ret;
}
bool is_prime[100000];
vector<int> prime;
void get_prime()
{
memset(is_prime,true,sizeof(is_prime));
for(int i=2;i<100000;i++)
{
if(is_prime[i])
prime.push_back(i);
for(int j=0;prime[j]*i<100000;j++)
{
is_prime[prime[j]*i]=false;
if(i%prime[j]) break;
}
}
}
int main(){
int n,k;
get_prime();
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&k);
int z=k;
for(int j=0;prime[j]*prime[j]<=z;j++)
{
int cnt=0;
while(z&&z%prime[j]==0)
{
z/=prime[j];
cnt++;
}
if(cnt)
{
game[prime[j]]|=(1<<(cnt-1));
}
}
if(z!=1) {
game[z]|=1;
}
}
int ans=0;
for(map<int,int>::iterator it=game.begin();it!=game.end();it++)
{
ans^=get_sg(it->second);
}
printf(ans?"Mojtaba":"Arpa");
putchar(10);
}