【链接】h在这里写链接
【题意】
给你n个数,a1,a2,……,an,两人轮流从中改数,每次选一个素数p和一个正整数k,将a1到an中所有可以被p^k整除的数除p^k。当玩家在他的回合把所有数都变成1后,该玩家就赢了。
【题解】
每个素数的游戏都是独立的。
比如2 3 4
p=2的游戏和p=3的游戏。会发现是独立的。
所以。把a[]里面的每个数字都分解一下质因数。
如4 = 2^2
则把dic[2] | = 1<<(2-1);
表示有一个数字是2^2(也即记录a[]里面有数字是2^2的倍数);
如9=3^2
则dic[3] |= 1<<(2-1);
表示有一个数字是3^2(也即记录a[]里面有数字是3^2的倍数);
然后把所有的这些素数都分别求出sg函数,然后异或一下就好。
对于某个素数p的游戏.
每次枚举k从1..30,表示要除的p^k;
显然,都除以p^k之后,p^i会变成p^(i-k);
则除了p^1..p^(k-1)不变之外,dic[p]的其他的位全都会往前移动k位。
则从状态x就转移成(x>>k) | ( ( 1<<( k-1 ) ) - 1);
如果新的状态和旧的状态一样,则break;
(sg[x] = mex(sg[i]),i是x能够通过一次转移到达的状态,知道这个就不难得到x的sg.);
【错的次数】
0
【反思】
每个素数构成了一个独立的游戏。
知道这个,就不难写了。
->状态压缩.
枚举每次的k,得到sg函数。
【代码】
#include <bits/stdc++.h> using namespace std; const int N = 1e2; int n, a[N+10]; map <int, int> dic; map <int, int> sg; int getsg(int x) { if (sg[x] != 0) { return sg[x]; } map <int,int> flag; for (int k = 1; k <= 30; k++) { int temp = (x >> k); temp |= x&((1 << (k-1)) - 1); if (temp == x) break; flag[getsg(temp)] = 1; } int now = 0; while (flag[now] == 1) { now++; } return sg[x] = now; } int main() { //freopen("F:\rush.txt", "r", stdin); ios::sync_with_stdio(0),cin.tie(0); cin >> n; for (int i = 1; i <= n; i++) { int x,p; cin >> x; for (p = 2; p*p <= x; p++) { int cnt = 0; while (x%p == 0) { x /= p; cnt++; } if (cnt) dic[p] |= 1 << (cnt - 1); } if (x > 1) { dic[x] |= 1; } } int sum = 0; for (auto temp : dic) sum ^= getsg(temp.second); if (sum) puts("Mojtaba"); else puts("Arpa"); return 0; }