P2490 [SDOI2011] 黑白棋 题解

k-nim 博弈板子题。

注意到白子只能向右移,黑子只能向左,那么可以将每对相邻的 白-黑 子看作一堆石子,石子个数为中间的空格数。每次可以从 \(1\sim d\) 个堆中取出若干石子。

考虑先手必胜的条件。将所有石子个数用二进制表示,那么一定存在一个 \(i\) 满足所有石子个数二进制表示下的第 \(i\) 位之和 \(\bmod(d+1)\neq 0\)

于是可以按位 dp,记录当前总石子数,每次枚举这一位为 \(1\) 的石子堆数,直接转移即可。

时间复杂度 \(\mathcal O(nk\log n)\)

参考代码:

#include<bits/stdc++.h>
#define ll long long
#define mxn 10003
#define md 1000000007
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define rept(i,a,b) for(int i=(a);i<(b);++i)
#define drep(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
int n,m,k,f[15][mxn][2];
ll ans,C[mxn+53][53];
signed main(){
	cin>>n>>m>>k,n-=m,m>>=1,k=min(k,m);
	C[0][0]=1;
	rep(i,1,n+m){
		C[i][0]=1;
		rep(j,1,min(i,m))C[i][j]=(C[i-1][j-1]+C[i-1][j])%md;
	}
	f[14][0][0]=1;
	drep(i,13,0){
		rep(j,0,n)rept(x,0,2)if(f[i+1][j][x]){
			rep(c,0,m)if(j+(1<<i)*c<=n){
				f[i][j+(1<<i)*c][x||c%(k+1)]=(f[i][j+(1<<i)*c][x||c%(k+1)]+f[i+1][j][x]*C[m][c])%md;
			}
		}
	}
	rep(i,0,n)ans=(ans+f[0][i][1]*C[n-i+m][m])%md;
	cout<<ans;
	return 0;
}
posted @ 2025-07-05 17:08  zifanwang  阅读(17)  评论(0)    收藏  举报