【Codeforces Round #432 (Div. 1) C】 Arpa and a game with Mojtaba

【链接】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;
}


posted @ 2017-10-04 18:44  AWCXV  阅读(108)  评论(0编辑  收藏  举报