版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/C20181220_xiang_m_y/article/details/88617166
题目传送门
题目分析:
题面似乎有点问题,如果白棋可以往左走,黑棋可以往右走,这游戏可能下不完。
所以白棋只能往右走,黑棋只能往左走。
又因为相邻的棋子颜色不同,所以这就变成了k/2个nim游戏的组合,同时有一点变化,就是每次不是只能从一堆中拿,而是至多d堆。
把每一堆的石子数量看成二进制,那么这个游戏的必败状态就是n堆石子每个二进制位上为1的堆数都是(d+1)的倍数。反之则是必胜态。
最后的状态是必败状态,且每个必胜态都可以变成必败态,而必败态无论怎么拿都会变成必胜态,所以得证。
题目要求必胜方案数,那么用总方案减去必败态,考虑DP分别处理每个二进制位,设
表示处理了前
位,用了
个石子的方案数,那么枚举当前位为1的堆数是(d+1)的几倍来转移:
最后统计答案的时候还要算上每对黑白棋的开始位置在哪里,把每对棋看成一组,去掉
个石子的空,就有:
总方案就是 ,减去ans就是最终答案。
Code:
#include<cstdio>
#include<cctype>
#include<algorithm>
#define maxn 100005
using namespace std;
inline void read(int &a){
char c;while(!isdigit(c=getchar()));
for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
}
const int mod = 1e9+7;
int n,K,d,f[17][10005],c[10005][105];
inline void add(int &a,int b){a+=b;if(a>mod) a-=mod;}
int main()
{
read(n),read(K),read(d),K>>=1;//先把K除2
c[0][0]=1;
for(int i=1;i<=n;i++){
c[i][0]=1;//这里j的循环注意取min
for(int j=min(i,2*K);j>=1;j--) c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
f[0][0]=1;
for(int i=0,bit=d+1;i<=14;i++,bit<<=1)//这里的bit直接从d+1开始
for(int j=n-2*K;j>=0;j--) if(f[i][j])
for(int k=min(K/(d+1),(n-2*K-j)/bit);k>=0;k--)
add(f[i+1][j+k*bit],1ll*f[i][j]*c[K][k*(d+1)]%mod);
int s=0;
for(int i=0;i<=n-2*K;i++) add(s,1ll*f[15][i]*c[n-i-K][K]%mod);
printf("%d",(c[n][2*K]-s+mod)%mod);
}