mayoko’s diary

プロコンとかいろいろ。

SRM 565 div1 med TheDivisionGame

med にしてはかなり簡単ですね。復帰戦だったのでちょうど良いけど。

問題

TopCoder Statistics - Problem Statement

U さんと K さんがゲームをする。ゲームは S = {L, L+1, ..., R-1, R} という数の集合を使って行う。そして,

  • 集合 S の中から何らかの数 X を取り出す。
  • X を割り切る数 Y を選ぶ(Y は 2 以上の整数)。
  • X を X/Y にして集合 S に戻す。

この一連の操作ができないほうが負けである。ある二つの自然数 A, B が与えられて, L, R は A <= L <= R <= B の範囲で動く。この中で先攻の U さんが勝つ [L, R] の組み合わせは何通りあるか。

解法

明らかに grundy 数っぽいですが, この grundy 数は素因数分解の指数の和になります。

この grundy 数の xor の和が 0 になるところが負けですが, これは累積和を使えば高速に計算できます。

const int MAX = 1000100;
int value[MAX];
int cnt[MAX];
int xorCnt[555];

class TheDivisionGame {
public:
    long long countWinningIntervals(int L, int R) {
        memset(cnt, 0, sizeof(cnt));
        for (int i = L; i <= R; i++)
            value[i-L] = i;
        for (int i = 2; i*i <= R; i++) {
            int l = L/i*i;
            if (l < L) l += i;
            for (int j = l; j <= R; j+= i) {
                while (value[j-L] % i == 0) {
                    cnt[j-L]++;
                    value[j-L] /= i;
                }
            }
        }
        for (int i = L; i <= R; i++) {
            if (value[i-L] > 1) cnt[i-L]++;
        }
        ll minus = 0;
        int now = 0;
        memset(xorCnt, 0, sizeof(xorCnt));
        xorCnt[0] = 1;
        for (int i = L; i <= R; i++) {
            now ^= cnt[i-L];
            minus += xorCnt[now];
            xorCnt[now]++;
        }
        ll d = R-L+1;
        return d*(d+1)/2 - minus;
    }
};