Alice和Bob将要进行如下的一场游戏。二人轮流操作,且Alice先行。
当轮到一个玩家的时候,他可以选择一枚金币,并将其向左移动任意多格,且至少移动一格。
金币不能被移出棋盘,也不能越过其它金币。
一个 (1 imes n) 的棋盘上最初摆放有 (m) 枚金币。其中每一枚金币占据了一个独立的格子,任意一个格子内最多只有一枚金币。
如果轮到一个玩家的时候他已经无法做出任何有效操作了(显然这个时候(m)枚金币恰好落在最左侧的(m)个格子中),则被判定为输家。已经知道Alice和Bob都是极致聪明的人,他们在任何局面下总能做出最优的操作。那么有多少初始状态能保证Alice必胜呢?
首先引入阶梯nim的概念,有(n)堆石子,从(0…n-1)标号,每次只能将第(i)堆石子若干个移动到第(i-1)堆石子。
注意到如果我们移动下标为偶数的石子,那么下一步对方一定可以模仿你把这堆石子再移动到下标为偶数上,所以可以把下标为偶数的石子忽略,而移动下标为奇数的石子可以看作把这堆石子移动到忽略的状态,也就是取走,所以阶梯nim相当于对下标为奇数的石子做nim游戏。
而回到这个题,我们可以把(m)个金币分成的(m+1)个距离看作(m+1)堆石子,石子总数恰好是(n-m),那么这个相当于从右往左的阶梯nim游戏,那么下标为奇数的位置有(frac{m+1}{2})个。
我们不妨设(m_1=frac{m+1}{2},m_2=m+1-m_1),分别表示下标为奇数和偶数的个数。
想知道必胜态的方案数,就必须要知道奇数石子异或和不为(0)的方案数,这个不好求,所以我们可以先求异或和为(0)的方案数,然后用总方案数减去异或和为(0)的方案数。
考虑到异或的性质,我们可以对每一位进行考虑,设(f_{i,j})表示从低到高考虑了(i)位,选了(j)个石子的方案数,考虑枚举这一位填多少个(1),那么奇数位置必须填偶数个(1),偶数位置可以随便填。
所以我们可以提前预处理出(c_i)表示填(i)个(1)的方案数,考虑枚举填奇数位置的个数,所以有如下式子:
然后枚举(x)表示当前位置填多少个(1),可以写出dp式子:
最后答案就是(egin{pmatrix}n\mend{pmatrix}-f_{18,n-m})。
还有(n<m)无解。
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 2e5;
const int p = 1e9 + 9;
using namespace std;
int n,m,f[19][N + 5],fac[N + 5],inv[N + 5],ans,c[N + 5];
int C(int n,int m)
{
if (n < 0 || m < 0 || m > n)
return 0;
return 1ll * fac[n] * inv[m] % p * inv[n - m] % p;
}
int Cc(int n,int m)
{
if (n == 0 && m == 0)
return 1;
return C(n + m - 1,n - 1);
}
int main()
{
scanf("%d%d",&n,&m);
if (m > n)
{
cout<<"0"<<endl;
return 0;
}
fac[0] = inv[1] = 1;
for (int i = 1;i <= N;i++)
fac[i] = 1ll * fac[i - 1] * i % p;
for (int i = 2;i <= N;i++)
inv[i] = 1ll * (p - p / i) * inv[p % i] % p;
inv[0] = 1;
for (int i = 1;i <= N;i++)
inv[i] = 1ll * inv[i - 1] * inv[i] % p;
f[0][0] = 1;
for (int i = 0;i <= m + 1;i++)
for (int j = 0;j <= i;j += 2)
c[i] += 1ll * C((m + 1) / 2,j) * C(m + 1 - (m + 1) / 2,i - j) % p,c[i] %= p;
for (int i = 0;i <= 17;i++)
for (int j = 0;j <= n - m;j++)
for (int x = 0;x * (1 << i) + j <= n - m && x <= m + 1;x++)
f[i + 1][j + x * (1 << i)] += 1ll * f[i][j] * c[x] % p,f[i + 1][j + x * (1 << i)] %= p;
ans = (C(n,m) - f[18][n - m]) % p;
cout<<(ans + p) % p<<endl;
return 0;
}