题目:
我是超链接
题解:
一开始读错题了 当一个棋子被挤到一端的时候就再也动不了了,那么根据白格子最左,黑格子最右。 我们首先考虑K=2,可以发现白格子尽量避免往左移动,黑格子避免向右移动,白格子的目标是向右移动把黑格子逼死,那么可以发现,只要两个格子的间隔不是0,都是先手胜的
K>2的情况同理,白棋是一定不会主动往左移的,黑棋也一定不会主动往右移,所以黑白之间的间隔一定会不断变小,这变成了什么?我们将间隔视为石子,那么这就相当于k/2堆石子,每人可以从1-d堆中取出若干个石子。这是一个经典的NimK问题。
一堆石子的SG函数为石子数 对每一个二进制位单独算,求SG函数每一个二进制位1的个数mod(d+1),如果都为0,则先手必败,否则必胜 一般的Nim就是k=1的情况
显然先手必败的情况好求,那我们用总方案数-必败的方案,总方案数显然是
Ckn
C
n
k
f[i][j]表示前i个二进制位放了j个石头必败的方案数,转移时枚举第i位放了
k∗2i
k
∗
2
i
个石子,乘一个组合数。
其实要注意统计答案的时候是
∑f[16][i]
∑
f
[
16
]
[
i
]
,因为石子的个数是不一定的(也就是间隔多大是不一定的) 数据范围要开longlong
代码:
using namespace std;
const
int mod=
1000000007;
LL c[
10005][
105],f[
20][
100005];
int main()
{
freopen(
"game5.in",
"r",stdin);
int n,k,d;scanf(
"%d%d%d",&n,&k,&d);
c[
0][
0]=
1;
for (
int i=
1;i<=n;i++)
{
c[i][
0]=
1;
for (
int j=
1;j<=k;j++) c[i][j]=(c[i-
1][j]+c[i-
1][j-
1])
%mod;
}
f[
0][
0]=
1;
for (
int i=
0;i<
16;i++)
for (
int j=
0;j<=n-k;j++)
for (
int l=
0;l
*(d+
1)<=k/
2 && (
1<<i)
*(d+
1)
*l<=j;l++)
f[i+
1][j]=(f[i+
1][j]+f[i][j-(
1<<i)
*(d+
1)
*l]
*c[k/
2][(d+
1)
*l])
%mod;
LL ans=
0;
for (
int i=
0;i<=n-k;i++) ans=(ans+f[
16][i]
*c[n-i-k/
2][k/
2]
%mod)
%mod;
printf(
"%lld",(c[n][k]-ans+mod)
%mod);
}