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;
}

浙公网安备 33010602011771号