luogu P2490 [SDOI2011]黑白棋

https://www.luogu.com.cn/problem/P2490
首先肯定是把相邻的黑白棋子之间的距离视为一堆棋子
然后问题就转换成了k-nim
解法是转换为二进制后 r i r_i ri表示第 i i i为1的个数%(k+1)
如果 r r r全部为0那先手必败,否则先手必胜
对于这题,这题是统计方案
显然必败的情况比较好DP
f [ i ] [ j ] 表 示 前 i 位 , 用 了 j 个 石 子 必 败 的 方 案 数 f[i][j]表示前i位,用了j个石子必败的方案数 f[i][j]ij
每次枚举 ( d + 1 ) (d+1) (d+1)的倍数 × 2 i \times 2^i ×2i转移即可
f [ i ] [ j + x ∗ ( d + 1 ) ∗ 2 i ] + = f [ i ] [ j ] ∗ C k x ∗ ( d + 1 ) f[i][j+x*(d+1)*2^i]+=f[i][j]*C_k^{x*(d+1)} f[i][j+x(d+1)2i]+=f[i][j]Ckx(d+1)
然后再用总方案数减去必败的即可(注意要乘个组合数)
code:

#include<bits/stdc++.h>
#define N 40005
#define mod 1000000007
using namespace std;
int c[N][205], dp[18][N], n, k, d;
int main() {
    scanf("%d%d%d", &n, &k, &d);
    for(int i = 0; i <= n; i ++) c[i][0] = 1;
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= 200; j ++)
            c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
    dp[0][0] = 1;
    for(int i = 0; i <= 15; i ++)
        for(int j = 0; j <= n - k; j ++) if(dp[i][j]) {
            for(int x = 0; j + (1 << i) * x * (d + 1) <= n - k && x * (d + 1) <= k / 2; x ++) {
                (dp[i + 1][j + (1 << i) * x * (d + 1)] += 1ll * dp[i][j] * c[k / 2][x * (d + 1)] % mod) %= mod;
            }
        }
    long long ans = c[n][k];
    for(int i = 0; i <= n - k; i ++)
        ans = (ans - 1ll * dp[16][i] * c[n - i - k + k / 2][k / 2] + mod) % mod;
    printf("%lld", ans);
    return 0;
}
posted @ 2021-07-17 09:27  lahlah  阅读(52)  评论(0编辑  收藏  举报