#博弈论,k-NIM,排列组合,dp#洛谷 2490 [SDOI2011] 黑白棋

题目传送门


分析

如果小A往左,那么小B也能往左使得小A往左的范围减小,因此小A只能往右,同样地小B只能往左。

那么相当于有 \(\frac{k}{2}\) 个堆石子,每次最多拿其中 \(d\) 堆石子,取完为负。问多少种办法先手必胜。

那么相当于把最多 \(n-k\) 的长度分成 \(\frac{k}{2}\) 份,这种 \(k-nim\) 必败条件是每个二进制位 \(1\) 的个数都是 \(k+1\) 的倍数。

这个用 dp 维护一下就可以了,设 \(dp[i][j]\) 表示末尾 \(i\) 位分配了 \(j\) 的长度的方案数,转移就是枚举这一位能增加多少长度,相当于背包计数。


代码

#include <iostream>
#include <string>
using namespace std;
const int N=10011,mod=1000000007;
int n,m,D,c[N][111],dp[15][N],ans;
int mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m>>D,dp[0][0]=c[0][0]=1;
	for (int i=1;i<=n;++i){
		c[i][0]=1;
		for (int j=1;j<=m;++j)
		    c[i][j]=mo(c[i-1][j-1],c[i-1][j]);
	}
	for (int i=0;i<14;++i)
	for (int j=0;j<=n-m;++j) if (dp[i][j])
	for (int k=0;j+(k<<i)<=n-m&&k<=(m>>1);k+=D+1)
		dp[i+1][j+(k<<i)]=mo(dp[i+1][j+(k<<i)],1ll*dp[i][j]*c[m>>1][k]%mod);
	for (int i=0;i<=n-m;++i) ans=mo(ans,1ll*dp[14][i]*c[n-i-(m>>1)][m>>1]%mod);
	cout<<mo(c[n][m],mod-ans);
	return 0;
}
posted @ 2025-06-29 10:27  lemondinosaur  阅读(58)  评论(0)    收藏  举报