problem
AliceAliceAlice 和 BobBobBob 又在玩游戏。
AliceAliceAlice 和 BobBobBob 在一个 1×n1\times n1×n 的网格图上玩游戏,网格图的 nnn 个格子中,有 kkk 个格子内被各放了一个棋子,其中 kkk 是一个偶数。
从左到右,这 kkk 个棋子依次被染色为红色、蓝色、红色、……、蓝色、红色、蓝色。其中红棋子是 AliceAliceAlice 的,蓝棋子是 BobBobBob 的。
两人轮流操作,需要遵循以下规则:
-
每一回合轮到某人操作时,这个人只能移动自己的棋子。
-
每个棋子只能在网格图内的格子之间移动,并且在移动棋子的时候,每个棋子不能跨过和这个棋子相邻的其它棋子。
-
每次最少需要移动 111 个棋子,最多可以移动 mmm 个棋子。
-
若移动多个棋子,则必须选择多个不同的棋子分别移动,每一个被选择移动的棋子不能停留在原位。
例如下图,当 AliceAliceAlice 操作时,若她要移动从左到右的第三个红棋子,那么她可以移动到的范围有下面这三个空心棋子的位置。
若某一回合,轮到某人操作时,这个人无法操作,那么这个人就输了。
现在 先手,假设两人均采用最优策略。
给定 n,k,mn,k,mn,k,m,请你求出有多少符合题意的初始局面,使得 AliceAliceAlice 必胜。对 1e9+71e9+71e9+7 取模。
solution
observation
:BobBobBob 只会向左移动,AliceAliceAlice 只会向右移动。
证明:因为如果 BobBobBob 向右移动,那么其左边的 AliceAliceAlice 的格子就会跟着移动【一直挤 BobBobBob 】,相互挤对方,但左边第一个是属于 AliceAliceAlice 的格子,所以最后一定是 AliceAliceAlice 会挤着 BobBobBob。AliceAliceAlice 向左移动也是同理。
所以把一个 AliceAliceAlice 和一个 BobBobBob 的格子绑成一对。
记 ai:a_i:ai: 第 iii 个红棋的位置,bi:b_i:bi: 第 iii 个蓝棋的位置,di=bi−ai−1d_i=b_i-a_i-1di=bi−ai−1。
这相当于有 k2\frac{k}{2}2k 堆石子,每堆石子个数为 did_idi。
这就转化成了经典的 nim-k
游戏【每次可以选择 1∼k1\sim k1∼k 堆石子,拿走任意的石子数量,最后不能操作者输。】
结论:当所有的 did_idi 转化成二进制后,每个二进制位上为 111 的个数 %(k+1)=0\%(k+1)=0%(k+1)=0 则是必败局面。
证明:
全 000 的局面一定是 PPP 局面。
任何一个 PPP 状态,经过一次操作以后必然会到达 NNN 状态。
在某一次移动中,至少有一堆被改变,即至少有一个二进制位被改变。
由于最多只能改变 kkk 堆石子,所以对于任何一个二进制位,111 的个数至多改变 kkk。
又有原先总数为 k+1k+1k+1 的整数倍,所以改变之后必然不可能仍是 k+1k+1k+1 的整数倍。
故在 PPP 状态下一次操作的结果必然是 NNN 状态。
任何 NNN 状态,总有一种操作使其变化成 PPP 状态。
从高位到低位考虑所有的二进制位。
假设用了某种方法,改变了 mmm 堆,使 iii 位之前的所有位都变为 k+1k+1k+1 的整数倍。
现在要证明总有一种方法让第 iii 位也恢复到 k+1k+1k+1 的整数倍(包括 000)。
显然,对于那些已经改变的 mmm 堆,当前的第 iii 位可以自由选择 111 或000【这只取决于操作时后拿走的石子个数】。
不考虑已经操作的 mmm 堆,记剩下的堆的第 iii 位上 111 的总和为 sumsumsum
分类讨论:
sum≤k−msum\le k-msum≤k−m
此时可以将这些堆上的 111 全部拿掉,然后让那 mmm 堆得 iii 位全部置成 000。
sum>k−msum>k-msum>k−m
此时我们在之前改变的 mmm 堆中选择 k+1−sumk+1-sumk+1−sum 堆,将他们的第 iii 位设置成 111。剩下的设置成000。
由于 k+1−sum<k+1−(k−m)<m+1⇒k+1−sum≤mk+1-sum<k+1-(k-m)<m+1\Rightarrow k+1-sum\le mk+1−sum<k+1−(k−m)<m+1⇒k+1−sum≤m,的确可以做到。
原博客证明
这样就可以 dpdpdp 了,用所有方案减去必败方案。
dpi,j:dp_{i,j}:dpi,j: 考虑到二进制位的 iii 位,一共用了 jjj 个石子的不合法方案数。
枚举下一个二进制有多少堆石子为 111,石子堆数必须是 m+1m+1m+1 的倍数。
dpi,j→dpi+1,j+2i+1⋅tdp_{i,j}\rightarrow dp_{i+1,j+2^{i+1}·t}dpi,j→dpi+1,j+2i+1⋅t
当然需要考虑是哪几堆,所以需要组合数。
最后要考虑将所有格子分成 kkk 堆,用隔板法计算。
code
#include <bits/stdc++.h>
using namespace std;
#define maxn 10005
#define int long long
#define mod 1000000007
int fac[maxn], inv[maxn], dp[maxn];
int n, k, m;int qkpow( int x, int y ) {int ans = 1;while( y ) {if( y & 1 ) ans = ans * x % mod;x = x * x % mod;y >>= 1;}return ans;
}void init() {fac[0] = inv[0] = 1;for( int i = 1;i <= n;i ++ ) fac[i] = fac[i - 1] * i % mod;inv[n] = qkpow( fac[n], mod - 2 );for( int i = n - 1;i;i -- ) inv[i] = inv[i + 1] * ( i + 1 ) % mod;
}int C( int n, int m ) { return fac[n] * inv[m] % mod * inv[n - m] % mod; }signed main() {freopen( "chess.in", "r", stdin );freopen( "chess.out", "w", stdout );scanf( "%lld %lld %lld", &n, &k, &m );init();int ans = C( n, k );n -= k, k >>= 1;dp[0] = 1;for( int w = 0;w < 15;w ++ )for( int i = n;i;i -- )for( int j = m + 1;( 1 << w ) * j <= i and j <= k;j += m + 1 )dp[i] = ( dp[i] + dp[i - ( 1 << w ) * j] * C( k, j ) ) % mod;for( int i = 0;i <= n;i ++ )ans = ( ans - dp[i] * C( n - i + k, k ) % mod + mod ) % mod;printf( "%lld\n", ans );return 0;
}