zoukankan      html  css  js  c++  java
  • 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
    ", a[0]);
        }
        return 0;
    }
    
  • 相关阅读:
    sklearn.model_selection.validation_curve 验证曲线
    sklearn.model_selection.learning_curve学习曲线
    sklearn.pipeline.Pipeline管道简化工作流
    什么时候需要做数据标准化
    小程序获取用户默认地址的代码
    微信小程序--更换用户头像/上传用户头像/更新用户头像
    小程序 image跟view标签上下会有间隙
    小程序宽100%,高自适应怎么做?
    小程序图片点击放大右滑
    小程序can't read property 'push' of undefined
  • 原文地址:https://www.cnblogs.com/artoriax/p/12187886.html
Copyright © 2011-2022 走看看