【题解】【BZOJ】BZOJ2281 黑白棋

BZOJ2281 黑白棋

BZOJ2281 黑白棋

1 题外话

博弈和DP不分家是吧

2 sol

由于先手不能左移,后手不能右移,那么如果两个棋子紧靠,双方都不能移动,且不影响其他棋子的移动。

将白-黑之间的间隔视为石子,问题转换成在(n/2) 个石子堆中任取(1,2,...,d) 个石子堆,每个可以取任意个

这是一个典型的k-nim问题,这里有详细阐述

给出结论:必败态是把每堆石子转换为二进制后,其中每一位上为1的石子堆数都能被(d+1)整除

那么我们设(f[i][j]) 表示考虑二进制前i位,放置了j个石头的方案数

转移如下

[f[i+1][j+k imes (d+1) imes 2^i]+=f[i][j]*C^{frac{K}{2}}_{k imes (d+1)}]

总方案减去必败即为必胜

3 code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;

const int N=100010;
const int mod=1000000007;

inline void read(int &x) {
    x=0;
    int f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') {
        if (ch=='-') {
            f=-1;
        }
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') {
        x=x*10+ch-'0';
        ch=getchar();
    }
    x*=f;
}


int n,k,d;
int C[N][210];
int f[30][N];
int bin[30];

inline void pre() {
    for(int i=0;i<=n;i++) {
        C[i][0]=1;
    }
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=min(2*k,i);j++) {
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
        }
    }
    bin[0]=1;
    for(int i=1;i<=20;i++) {
        bin[i]=bin[i-1]<<1;
    }
}

inline int c(int n,int m) {
    if (m>n-m) {
        m=n-m;
    }
    return C[n][m];
}

signed main() {
    read(n),read(k),read(d);
    k/=2;
    pre();
    f[0][0]=1;
    for(int i=0;i<15;i++) {
        for(int j=0;j<=n-2*k;j++) {
            for(int g=0;g*(d+1)<=k&&j+g*(d+1)*bin[i]<=n-2*k;g++) {
                f[i+1][j+g*(d+1)*bin[i]]+=f[i][j]*c(k,g*(d+1));
                f[i+1][j+g*(d+1)*bin[i]]%=mod;
            }
        }
    }
    int ans=c(n,k*2);
    for(int i=0;i<=n-2*k;i++) {
        ans-=(f[15][i]*c(n-i-k,k))%mod;
        ans=((ans%mod)+mod)%mod;
    }
    ans=((ans%mod)+mod)%mod;
    printf("%lld
",ans);
    return 0;
}

Author: tt66ea

Created: 2021-08-13 周五 11:25

Validate

原文地址:https://www.cnblogs.com/tt66ea-blog/p/15136533.html