P2490 [SDOI2011] 黑白棋 题解

笔记列表

  • 高级题目,好高级好高级好搞机好搞机好

  • 首先有一个显而易见的转化

  • 因为黑白棋子是交替的,又因为只能把棋子往对方的方向动

  • 发现可以转化成有 \(\frac{k}{2}\) 堆,每一次可以从 \(d\) 堆中取任意数量的石子,每堆互相独立,可以取不同数量,最多有 \(n-\frac{k}{2}\)个石子

  • 不能取的输

  • 问有多少种先手必胜的局面

  • 首先这其实是一个 \(k-nim\) 游戏

  • 记数 \(x\) 的二进制下的第 \(k\) 位为 \((x)_k\)。结论: \(\forall s,(\sum_{i=1}^n(a_i)s)\equiv 0 \ (mod \;k+1)\),先手必败,否则先手必胜

  • 是的,我不会证明

  • \(b_s\equiv (\sum_{i=1}^n(a_i)_s) (mod \;\;\; k + 1)\)

  • \(f_{i,j}\) 表示考虑了 \(0\)\(i-1\) 位的\(b\) 都为0的方案数,当前放了 \(j\)个石子

  • 现在要满足是\((k+1)\)的倍数

  • \(f_{i+1,j+2^ix(d+1)}\) += \(\binom{\frac{k}{2}}{x(d+1)} \times f_{i,j}\)

  • 也就是从这些堆中选出 \(x(d+1)\) 堆在这一位填 \(1\)

  • 不合法的答案就是 \(\sum_j(f_{14,j} \times \binom{tot-j+m}{m})\)

  • 其中 \(tot = n - \frac{k}{2}\) 表示总石子数,\(m=\frac{k}{2}\)表示石子堆数

  • 最后的组合数表示将剩余的\(tot-j\)个石子,插到\(m+1\)个空位里面,因为那些空格你不一定要放入到匹配的黑棋和白棋之间,也可以放到到两边和不匹配的中间

  • 现在算出了不合法的方案总数(是的之前的\(dp\)都是逆推,算不合法也就是先手必败的方案数)

  • 最后用 \(\binom{n}{k}-ans\)就是答案

#include<bits/stdc++.h>
#define int long long
#define fore(i,a,b) for( int i=(a); i<=(b); ++i)
#define repe(i,a,b) for( int i=(a); i>=(b); --i)
using namespace std;
const int N = 5e4 + 10;
const int H = 15;
const int mod = 1e9 + 7;
int n, k, d, m;
int tot;
int f[H][N];
int fac[N], inv[N];
int qpow(int x,int y) {
    int res = 1;
    while(y) {
        if(y & 1) res = res * x % mod;
        x = x * x % mod;
        y >>= 1;
    }
    return res;
}
void init() {
    int nn = 5e4;
    fac[0] = 1;
    fore(i, 1, nn) fac[i] = fac[i - 1] * i % mod;
    inv[nn] = qpow(fac[nn], mod - 2);
    repe(i, nn - 1, 0) inv[i] = inv[i + 1] * (i + 1) % mod;
}
inline int C(int A,int B) {
    if(B > A) return 0;
    if(B == 0) return 1;
    return fac[A] * inv[B] % mod * inv[A - B] % mod;
}
int ans = 0;
signed main(){
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	// freopen(".in","r",stdin);
	// freopen(".out","w",stdout);
	cin >> n >> k >> d;
    m = k / 2; tot = n - k;
    init();
    f[0][0] = 1;
    fore(i, 0, 13) { // shizi tot = n - k
        fore(j, 0, tot) {
            for(int s = 0; s * (d + 1) <= m; s ++) {
                int nxt = j + (1 << i) * s * (d + 1);
                if(nxt > tot) break;
                f[i + 1][nxt] = (f[i + 1][nxt] + f[i][j] * C(m, s * (d + 1)) % mod) % mod;
            }
        }
    }
    fore(j, 0, tot) {
        int res = f[14][j];
        res = res * C(tot - j + m, m) % mod;
        ans = (ans + res) % mod;
    }  
    ans = C(n, k) - ans;
    ans = (ans + mod) % mod;
    cout << ans % mod << '\n';
	return 0;
}

posted @ 2026-09-18 09:04  wmq2012  阅读(7)  评论(0)    收藏  举报