对弈(nim-k游戏博弈)

problem

A l i c e Alice Alice B o b Bob Bob 又在玩游戏。

A l i c e Alice Alice B o b Bob Bob 在一个 1 × n 1\times n 1×n 的网格图上玩游戏,网格图的 n n n 个格子中,有 k k k 个格子内被各放了一个棋子,其中 k k k 是一个偶数。

从左到右,这 k k k 个棋子依次被染色为红色、蓝色、红色、……、蓝色、红色、蓝色。其中红棋子是 A l i c e Alice Alice 的,蓝棋子是 B o b Bob Bob 的。

两人轮流操作,需要遵循以下规则:

  • 每一回合轮到某人操作时,这个人只能移动自己的棋子。

  • 每个棋子只能在网格图内的格子之间移动,并且在移动棋子的时候,每个棋子不能跨过和这个棋子相邻的其它棋子。

  • 每次最少需要移动 1 1 1 个棋子,最多可以移动 m m m 个棋子。

  • 若移动多个棋子,则必须选择多个不同的棋子分别移动,每一个被选择移动的棋子不能停留在原位。

例如下图,当 A l i c e Alice Alice 操作时,若她要移动从左到右的第三个红棋子,那么她可以移动到的范围有下面这三个空心棋子的位置。

在这里插入图片描述

若某一回合,轮到某人操作时,这个人无法操作,那么这个人就输了。

现在 先手,假设两人均采用最优策略。

给定 n , k , m n,k,m n,k,m,请你求出有多少符合题意的初始局面,使得 A l i c e Alice Alice 必胜。对 1 e 9 + 7 1e9+7 1e9+7 取模。

solution

observation B o b Bob Bob 只会向左移动, A l i c e Alice Alice 只会向右移动。

证明:因为如果 B o b Bob Bob 向右移动,那么其左边的 A l i c e Alice Alice 的格子就会跟着移动【一直挤 B o b Bob Bob 】,相互挤对方,但左边第一个是属于 A l i c e Alice Alice 的格子,所以最后一定是 A l i c e Alice Alice 会挤着 B o b Bob Bob A l i c e Alice Alice 向左移动也是同理。

所以把一个 A l i c e Alice Alice 和一个 B o b Bob Bob 的格子绑成一对。

a i : a_i: ai: i i i 个红棋的位置, b i : b_i: bi: i i i 个蓝棋的位置, d i = b i − a i − 1 d_i=b_i-a_i-1 di=biai1

这相当于有 k 2 \frac{k}{2} 2k 堆石子,每堆石子个数为 d i d_i di

这就转化成了经典的 nim-k 游戏【每次可以选择 1 ∼ k 1\sim k 1k 堆石子,拿走任意的石子数量,最后不能操作者输。】

结论:当所有的 d i d_i di 转化成二进制后,每个二进制位上为 1 1 1 的个数 % ( k + 1 ) = 0 \%(k+1)=0 %(k+1)=0 则是必败局面。

证明:

  • 0 0 0 的局面一定是 P P P 局面。

  • 任何一个 P P P 状态,经过一次操作以后必然会到达 N N N 状态。

    • 在某一次移动中,至少有一堆被改变,即至少有一个二进制位被改变。

      由于最多只能改变 k k k 堆石子,所以对于任何一个二进制位, 1 1 1 的个数至多改变 k k k

      又有原先总数为 k + 1 k+1 k+1 的整数倍,所以改变之后必然不可能仍是 k + 1 k+1 k+1 的整数倍。

      故在 P P P 状态下一次操作的结果必然是 N N N 状态。

  • 任何 N N N 状态,总有一种操作使其变化成 P P P 状态。

    • 从高位到低位考虑所有的二进制位。

      假设用了某种方法,改变了 m m m 堆,使 i i i 位之前的所有位都变为 k + 1 k+1 k+1 的整数倍。

      现在要证明总有一种方法让第 i i i 位也恢复到 k + 1 k+1 k+1 的整数倍(包括 0 0 0)。

      显然,对于那些已经改变的 m m m 堆,当前的第 i i i 位可以自由选择 1 1 1 0 0 0【这只取决于操作时后拿走的石子个数】。

      不考虑已经操作的 m m m 堆,记剩下的堆的第 i i i 位上 1 1 1 的总和为 s u m sum sum

      • 分类讨论:

        • s u m ≤ k − m sum\le k-m sumkm

          此时可以将这些堆上的 1 1 1 全部拿掉,然后让那 m m m 堆得 i i i 位全部置成 0 0 0

        • s u m > k − m sum>k-m sum>km

          此时我们在之前改变的 m m m 堆中选择 k + 1 − s u m k+1-sum k+1sum 堆,将他们的第 i i i 位设置成 1 1 1。剩下的设置成 0 0 0

          由于 k + 1 − s u m < k + 1 − ( k − m ) < m + 1 ⇒ k + 1 − s u m ≤ m k+1-sum<k+1-(k-m)<m+1\Rightarrow k+1-sum\le m k+1sum<k+1(km)<m+1k+1summ,的确可以做到。

原博客证明

这样就可以 d p dp dp 了,用所有方案减去必败方案。

d p i , j : dp_{i,j}: dpi,j: 考虑到二进制位的 i i i 位,一共用了 j j j 个石子的不合法方案数。

枚举下一个二进制有多少堆石子为 1 1 1,石子堆数必须是 m + 1 m+1 m+1 的倍数。

d p i , j → d p i + 1 , j + 2 i + 1 ⋅ t dp_{i,j}\rightarrow dp_{i+1,j+2^{i+1}·t} dpi,jdpi+1,j+2i+1t

当然需要考虑是哪几堆,所以需要组合数。

最后要考虑将所有格子分成 k k k 堆,用隔板法计算。

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;
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值