显然,如果白棋往左,黑棋往右,最后肯定会两两碰在一起,就像这样:
红框框起来的是会碰在一起的棋子。(我们把会碰到一起的棋子称为一对棋子)
如下图就是碰在一起的一种情况:
那么现在假设是 A A A 遇到了这种情况,那么无论他操作的是白棋或黑棋,他肯定会输。因为另一个人可以操控棋子跟着 A A A 的棋子走,一直保持棋子两两紧逼的状态,直到所有棋子都堆在一边,这时 A A A 就无路可走了,失败。
不妨设开始前每对棋子之间的距离为 a 1 , a 2 , … , a k 2 a_1,a_2,dots,a_{frac{k}{2}} a1,a2,…,a2k,那么原题就可以转化成一个 k-nim 游戏:现在有 k 2 frac{k}{2} 2k 堆石子,第 i i i 堆石子的石子个数是 a i a_i ai,现在两人轮流进行如下操作:在这之中任意选取 1 ∼ d 1sim d 1∼d 堆石子,对于选取的这几堆石子中的每堆石子,能拿走任意正整数个石子。最后不能进行操作为失败。
这种问题有一个结论:将当前状态的 a i a_i ai 用二进制表示,设 s i s_i si 表示 a 1 ∼ a k 2 a_1sim a_{frac{k}{2}} a1∼a2k 中二进制下第 i i i 位 1 1 1 的个数, s i ≡ s i ′ ( m o d k + 1 ) s_iequiv s_i'pmod {k+1} si≡si′(modk+1)。若所有的 s i ′ s_i' si′ 都为 0 0 0,则这个状态为必败状态,否则为必胜状态。
证明:
-
a i a_i ai 全部为 0 0 0 的状态是必败状态,且此时所有的 s i ′ s_i' si′ 都为 0 0 0。
-
设状态 P P P 是 s i ′ s_i' si′ 全部为 0 0 0 的状态,状态 N N N 是存在 s i ′ s_i' si′ 不为 0 0 0 的状态。
假设当前状态为 P P P 状态。
显然,由于我至少要在一个堆里面取石子,所以某些 a i a_i ai 的一定会改变。所以首先,某些 s i s_i si 一定会改变。然后又由于一次最多只能改变 d d d 个堆,所以对于每一个 s i s_i si,最多只会加上或减去 d d d。又由于一开始 s i ≡ 0 ( m o d d + 1 ) s_iequiv0pmod{d+1} si≡0(modd+1),所以变化后的 s i s_i si 一定不可能满足 s i ≡ 0 ( m o d d + 1 ) s_iequiv0pmod{d+1} si≡0(modd+1)。
也就是说,对于任意的 P P P 状态,一定只能转移成 N N N 状态。
-
假设当前状态为 N N N 状态,那么我们要证明的目标是:对于任意的 N N N 状态,一定存在一种转移方式转移成 P P P 状态。
我们从高位到低位地考虑所有的二进制位。显然,最高位的石子我们要全部取走。假设当前位为第 i i i 位,且已经满足了 s j ′ = 0 s_j'=0 sj′=0( j > i j>i j>i),并且改变了 m m m 个堆的石子数量( m ≤ d mleq d m≤d)。
那么我们现在就是要证明:是否有一种操作,能使得 s i ′ = 0 s_i'=0 si′=0,且新改变的堆加上以前改变过的堆的总堆数 m ′ ≤ d m'leq d m′≤d。(类似数学归纳法)
有一个比较显然的性质:对于那些已经改变的 m m m 堆,我改变这些堆的低位不会影响到高位的 s j ′ s_j' sj′。
设在这 m m m 堆中,每一堆所含石子个数的第 i i i 位上共有 a a a 个 1 1 1, b b b 个 0 0 0。
分情况讨论:
-
a ≥ s i ′ ageq s_i' a≥si′。那我们就可以在这 m m m 堆中找到 s i ′ s_i' si′ 个第 i i i 位为 1 1 1 的堆并取走这一位所代表的石子(也就是 2 i 2^i 2i 个石子),显然根据我们刚刚提到的性质,取走这些石子不会影响高位的 s j ′ s_j' sj′。(也就是让 s i ′ ← 0 s_i'gets 0 si′←0)
-
b ≥ ( d + 1 ) − s i ′ bgeq (d+1)-s_i' b≥(d+1)−si′。那我们就可以在这 m m m 堆中找到 ( d + 1 ) − s i ′ (d+1)-s_i' (d+1)−si′ 个第 i i i 位为 0 0 0 的堆并加上这一位所代表的石子(也就是 2 i 2^i 2i 个石子)(就算加上了这些石子,对于这一堆来说,石子总数还是减的,因为我们一开始减去了最高位所代表的石子),显然根据我们刚刚提到的性质,取走这些石子不会影响高位的 s j ′ s_j' sj′。(也就是让 s i ′ ← d + 1 s_i'gets d+1 si′←d+1)
-
上述两种情况都不满足,也就是说 a < s i ′ a< s_i' a<si′,那么我们先取走这 a a a 堆中第 i i i 位的石子,然后再从这 m m m 堆之外的堆里面找 s i ′ − a s_i'-a si′−a 个第 i i i 位为 1 1 1 的堆并取出它第 i i i 位的石子,总堆数变成了 m + s i ′ − a = b + s i ′ ≤ d + 1 − s i ′ + s i ′ = d + 1 m+s_i'-a=b+s_i'leq d+1-s_i'+s_i'=d+1 m+si′−a=b+si′≤d+1−si′+si′=d+1,所以这种取法是合法的。
综上所述,我们可以保证对于任意的 N N N 状态,一定存在一种转移方式转移成 P P P 状态。
-
综上所述,我们就可以证明 P P P 状态是必败状态, N N N 状态是必胜状态,结论成立。
接下来就是如何计算方案。
设 d p ( i , j ) dp(i,j) dp(i,j) 表示前 i − 1 i-1 i−1 位每一位的异或和均为 0 0 0,已经有 j j j 个石子的方案数。(也就是必败方案数)
枚举 a 1 ∼ a k 2 a_1sim a_frac{k}{2} a1∼a2k 的第 i i i 位共有多少个 1 1 1,且满足个数是 ( d + 1 ) (d+1) (d+1) 的倍数(也就是枚举 ( d + 1 ) (d+1) (d+1) 的 x x x 倍)。
然后在 k 2 frac{k}{2} 2k 个堆里面取 x ( d + 1 ) x(d+1) x(d+1) 个,把他们的第 i i i 位设为 1 1 1,总方案数是 ( k 2 x ( d + 1 ) ) dbinom{frac{k}{2}}{x(d+1)} (x(d+1)2k)。
最后我们还要枚举堆在哪里,也就是 ( n − j − k 2 k 2 ) dbinom{n-j-frac{k}{2}}{frac{k}{2}} (2kn−j−2k)。
然后 dp 一下就好了。
代码如下:
#include<bits/stdc++.h>
#define LN 20
#define K 210
#define N 10010
#define ll long long
#define int long long
#define mod 1000000007
using namespace std;
int n,k,d;
int C[N][K],dp[LN][N];
signed main()
{
scanf("%lld%lld%lld",&n,&k,&d);
C[0][0]=1;
for(int i=1;i<=n;i++)
{
C[i][0]=1;
for(int j=1;j<=200;j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
dp[0][0]=1;
for(int i=0;i<=16;i++)
{
ll t=(1ll<<i);
for(int j=0;j<=n-k;j++)
for(int x=0;t*x*(d+1)<=n-k&&x*(d+1)<=k/2;x++)
dp[i+1][j+t*x*(d+1)]=(dp[i+1][j+t*x*(d+1)]+(1ll*dp[i][j]*C[k/2][x*(d+1)])%mod)%mod;
}
ll ans=0;
for(int i=0;i<=n-k;i++)
ans=(ans+1ll*dp[17][i]*C[n-i-k/2][k/2]%mod)%mod;
printf("%lld
",((C[n][k]-ans)%mod+mod)%mod);
return 0;
}