阶梯Nim
有 n 堆石子, 标号为 1~n, 每次可以从第 i 堆中拿走一部分放到第 i-1 堆中, 或者把第 1 堆中的石子拿走一部分, 无法操作者算输。
结论: 等价于把所有奇数的位置拿出来, 进行普通 Nim 游戏。
普通 Nim 的异或和为 0 的计数
n 个石子放到 m 个容器, 使得所有容器的石子数的异或和为 0, 求方案数。
考虑每个容器的石子个数拆成二的幂的和, 然后按位考虑, 对于每一位 i 往偶数个容器里每一个放 2i 个石子。考虑 dp, dp(i,j) 表示从低到高放完前 i 位, 放了 j 个石子, 枚举这一位的 2i 的个数 k, 转移就是:
[dp(i,j) = sum_{kequiv 0mod 2} inom mk dp(i-1,j-k2^i)
]
对于本题,转化成阶梯 Nim就是:把 n-m 个石子装进 m+1 个容器(标号为 1~m+1), 使得偶数标号的容器的石子数的异或和不为 0 的方案数。考虑直接统计异或和为 0 的方案数, 最后用总方案数减去。
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N = 2e5 + 3, mo = 1e9 + 9; // prime
LL qpow (LL a, LL b) {
a %= mo;
LL res = 1ll;
for (; b; b >>= 1, a = a * a % mo)
if (b & 1) res = res * a % mo;
return res;
}
LL fac[N], ifac[N];
LL C (int n, int m) {
if (n < m) return 0ll;
return fac[n] * ifac[m] % mo * ifac[n - m] % mo;
}
LL D (int n, int m) { // x1...xm = n + m - 1 m
return C (n + m - 1, m - 1);
}
void Inc (LL &x, LL y) { x = (x + y) % mo; }
int b;
LL dp[21][N];
void gao (int n, int m) { // n stone -> m mol, naive Nim
dp[0][0] = 1ll;
for (b = 0; n >> b; ++ b) {
for (int j = 0; j <= n; j += 2) {
for (int k = 0; k <= m; k += 2) {
if (j + (k << b) > n) break;
Inc (dp[b + 1][j + (k << b)], dp[b][j] * C (m, k) % mo);
}
}
}
}
int main()
{
int n, m;
cin >> n >> m;
if (n < m) { putchar ('0'); return 0; }
int l = n + m;
fac[0] = 1ll;
for (int i = 1; i <= l; ++ i) fac[i] = (LL)i * fac[i - 1] % mo;
ifac[l] = qpow (fac[l], mo - 2);
for (int i = l; i >= 1; -- i) ifac[i - 1] = (LL)i * ifac[i] % mo;
gao (n - m, (m + 1) / 2);
n = n - m, m = m + 1;
int h = m - m / 2;
LL ans = 0ll;
for (int i = 0; i <= n; ++ i) ans += dp[b][i] * D (n - i, h) % mo, ans %= mo;
cout << (D (n, m) + mo - ans) % mo;
return 0;
}