#博弈论,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;
}

浙公网安备 33010602011771号