散落星河的记忆🌠
Published on 2017-09-02 11:31 in 暂未分类 with 散落星河的记忆🌠

[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

\(1\le n\le 10^9, 2\le m\le 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\n", fastpow(nn - 1));
	}
	return 0;
}
posted @ 2019-01-30 19:24  散落星河的记忆🌠  阅读(93)  评论(0编辑  收藏  举报