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

    输入包含多组数据,每组数据一行两个正整数 (n)(m)

    Output

    每行一个整数表示答案。

    Sample Input

    3 7
    4 13
    

    Sample Output

    6
    120
    

    HINT

    (1le nle 10^9, 2le mle 50000)

    不超过 (80) 组数据。

    Solution

    NIM 游戏先手必胜的条件是每堆石子数的异或和为 (0)

    相当于将一个只有质数次项的系数是 (1),其他项的系数是 (0)(m) 次多项式 (fwt) 异或卷积 (n) 次,最后输出常数项。

    Code

    #include <cstdio>
    
    const int mod = 1000000007, inv = 500000004;
    int n, m, nn, p[5135], np[50001], tot, a[65540], b[65540];
    
    void euler(int n) {
    	for (int i = 2; i <= n; ++i) {
    		if (!np[i]) p[++tot] = i;
    		for (int j = 1; j <= tot && i * p[j] <= n; ++j) {
    			np[i * p[j]] = 1;
    			if (i % p[j] == 0) break;
    		}
    	}
    }
    void fwt(int *a, int f) {
    	for (int i = 1; i < n; i <<= 1)
    		for (int j = 0, r = i << 1; j < n; j += r)
    			for (int k = 0; k < i; ++k) {
    				int x = a[j + k], y = a[i + j + k];
    				a[j + k] = 1LL * (x + y) % mod, a[i + j + k] = 1LL * (x + mod - y) % mod;
    				if (f == -1) a[j + k] = 1LL * a[j + k] * inv % mod, a[i + j + k] = 1LL * a[i + j + k] * inv % mod;
    			}
    }
    int fastpow(int k) {
    	for (; k; k >>= 1) {
    		if (k & 1) for (int i = 0; i < n; ++i) b[i] = 1LL * b[i] * a[i] % mod;
    		for (int i = 0; i < n; ++i) a[i] = 1LL * a[i] * a[i] % mod;
    	}
    	fwt(b, -1); return b[0];
    }
    int main() {
    	euler(50000);
    	while (~scanf("%d%d", &nn, &m)) {
    		for (n = 1; n <= m; n <<= 1) {}
    		for (int i = 0; i < n; ++i) a[i] = 0;
    		for (int i = 1; i <= tot && p[i] <= m; ++i) a[p[i]] = 1;
    		fwt(a, 1);
    		for (int i = 0; i < n; ++i) b[i] = a[i];
    		printf("%d
    ", fastpow(nn - 1));
    	}
    	return 0;
    }
    
    A man can be destroyed, but not defeated.
  • 相关阅读:
    #pragma 预处理指令
    C++类继承中的构造函数和析构函数 调用顺序
    static_cast与dynamic_cast转换 最简单的理解
    std::map的insert和下标[]访问
    C++有没有string转化int的函数,怎样转换
    c++中在一个类中定义另一个只有带参数构造函数的类的对象
    c++二进制文件的读写
    C++ 包含头文件 和 宏的使用 和 条件编译
    C++ 前置声明 和 包含头文件 如何选择
    C语言 gets()和scanf()函数的区别
  • 原文地址:https://www.cnblogs.com/fly-in-milkyway/p/10329231.html
Copyright © 2011-2022 走看看