BZOJ-4589 Hard Nim

Description

Claris和NanoApe在玩石子游戏,他们有n堆石子,规则如下:

  1. Claris和NanoApe两个人轮流拿石子,Claris先拿。

  2. 每次只能从一堆中取若干个,可将一堆全取走,但不可不取,拿到最后1颗石子的人获胜。

不同的初始局面,决定了最终的获胜者,有些局面下先拿的Claris会赢,其余的局面Claris会负。

Claris很好奇,如果这n堆石子满足每堆石子的初始数量是不超过m的质数,而且他们都会按照最优策略玩游戏,那么NanoApe能获胜的局面有多少种。

由于答案可能很大,你只需要给出答案对\(10^9+7\)取模的值。

Input

输入文件包含多组数据,以EOF为结尾。

对于每组数据:

共一行两个正整数n和m。

每组数据有\(1<=n<=10^9, 2<=m<=50000\)

不超过80组数据。

Sample Input

3 7
4 13

Sample Output

6
120

题解

首先需要一点博弈论的知识

对于一堆个数为\(x\)的石子,其状态即为\(SG(x)=x\),由SG定理可知,n堆即为全部异或起来,也就是全部异或后若和为0,则先手必败,否则先手必胜

那么这个题就转化为从小于m的质数中挑n个,异或和为0的有几种

于是就可以fwt做了

先fwt求得点值表示,由于没有精度损失,可以直接快速幂求n次方,再求逆即可

fwt用于计算位运算的卷积,如

\[C_k=\sum_{i|j=k}A_i∗B_j \\C_k=\sum_{i\&j=k}A_i∗B_j \\C_k=\sum_{i∧j=k}A_i∗B_j \]

原理这个东西...搞不太懂,记模板好了,和fft挺像的

下面代码中fwt的与、或和异或都给出了,其中异或是取模意义下的

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
ll qpow(ll a, ll b) {
    ll ans = 1;
    while (b) {
        if (b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}

const ll inv2 = qpow(2, mod - 2);

void fwt_and(ll a[], int len, int op) {
    for (int h = 2; h <= len; h <<= 1) {
        for (int j = 0; j < len; j += h) {
            for (int k = j; k < j + h / 2; k++) {
                a[k + h / 2] += a[k] * op;
            }
        }
    }
}

void fwt_or(ll a[], int len, int op) {
    for (int h = 2; h <= len; h <<= 1) {
        for (int j = 0; j < len; j += h) {
            for (int k = j; k < j + h / 2; k++) {
                a[k] += a[k + h / 2] * op;
            }
        }
    }
}

void fwt_xor(ll a[], int len, int op) {
    for (int h = 2; h <= len; h <<= 1) {
        for (int j = 0; j < len; j += h) {
            for (int k = j; k < j + h / 2; k++) {
                ll u = a[k], t = a[k + h / 2];
                a[k] = (u + t) % mod;
                a[k + h / 2] = (u - t + mod) % mod;
                if (op == -1) {
                    a[k] = a[k] * inv2 % mod;//若不用取模,直接将inv2改成/2即可
                    a[k + h / 2] = a[k + h / 2] * inv2 % mod;
                }
            }
        }
    }
}
const int N = 2e5 + 50;
int notprime[N], prime[N];

ll a[N];
int main() {
    int cnt = 0;
    for (int i = 2; i < N; i++) {
        if (!notprime[i]) prime[++cnt] = i;
        for (int j = 1; j <= cnt && i * prime[j] < N; j++) {
            notprime[i * prime[j]] = 1;
            if (i % prime[j] == 0) break;
        }
    }
    int n, m;
    while (~scanf("%d%d", &n, &m)) {
        int len = 1;
        while (len <= m) len <<= 1;
        for (int i = 0; i < len; i++) a[i] = 0;
        for (int i = 1; i <= cnt && prime[i] <= m; i++) {
            a[prime[i]] = 1;
        }
        fwt_xor(a, len, 1);
        for (int i = 0; i < len; i++) {
            a[i] = qpow(a[i], n);
        }
        fwt_xor(a, len, -1);
        printf("%lld\n", a[0]);
    }
    return 0;
}
posted @ 2020-01-13 16:24  Artoriax  阅读(73)  评论(0编辑  收藏  举报