2368. 黑白棋
Description
小A和小B又想到了一个新的游戏。
这个游戏是在一个1*n的棋盘上进行的,棋盘上有k个棋子,一半是黑色,一半是白色。
最左边是白色棋子,最右边是黑色棋子,相邻的棋子颜色不同。
小A可以移动白色棋子,小B可以移动黑色的棋子,他们每次操作可以移动1到d个棋子。
每当移动某一个棋子时,这个棋子不能跨越两边的棋子,当然也不可以出界。当谁不可以操作时,谁就失败了。
小A和小B轮流操作,现在小A先移动,有多少种初始棋子的布局会使他胜利呢?
Input
共一行,三个数,n,k,d。
Output
输出小A胜利的方案总数。答案对1000000007取模。
Sample Input
10 4 2
Sample Output
182
Data Constraint
对于30%的数据,有 k=2。
对于100%的数据,有1<=d<=k<=n<=10000, k为偶数,k<=100。
题解
我们首先发现这题直接做不好做。
那么找找有什么突破口。
我们发现,当每个白棋右边都贴着个黑棋,那么先手必败。
那么白棋黑棋就都是要尽量往左边白棋或右边黑棋靠。
我们发现,一个白棋往左移或一个黑棋往右走都没有意义,因为越往右边(左边)走越对黑棋(白棋)不利。
那么我们就可以看到左边白棋对应右边黑棋中间这一段很重要。
为了好表示,把红色格子连成的块说成关键堆,格子为石子。
在此,我们可以发现,每次黑棋或白棋走一格,都是缩短了关键堆的大小。
那么,我们就得到了k/2个关键堆,每次相当于取出其中1~d个堆中任意一个石子。
没得取时失败。
这就是一个典型的K·NIM游戏。
具体介绍可以看到:https://blog.****.net/weixinding/article/details/7321139
于是我们得到了个结论:
当每个关键堆的大小用二进制表示出来时,对于每个位置上的1的个数,如果%(d+1)=0,那么就表示当前状态必败。
差值表示一下就可以发现,必胜态就是总状态-必败态
总状态就是C(k,n)
接下来我们考虑如何求必败态。
我们设f[i,j]表示当前把k/2个关键堆二进制表示出来后,组成1~i位满足必败时用了j个空格。
于是乎,考虑转移。
我们发现,转移到下一位时,就是要把下一位的1的个数变成(d+1)的倍数。
那么枚举l表示下一位有l*(d+1)个1。
然后由于可以随意摆放1的位置,那么乘上一个组合数C(l*(d+1),k/2)即可。
转移方程:
然而这样算完之后,我们还要考虑如何在n中插入这几个堆。
那么枚举堆的总大小j。
我们把上图中堆的组成部分给删掉。
得到个空格,那么问题就转化为在这么多个空格中插入k/2挡板
方案为
最后答案转移:
减一减即可。
代码:
uses math;
var
i,j,k,l,n,m,d,s1,s2,now:longint;
ans,gs:int64;
mo:int64=1000000007;
f:array[0..18,0..20000] of int64;
g:array[1..10000,1..100] of int64;
jc:array[1..10000] of int64;
mi:array[0..10000] of int64;
zhs:array[0..10000,0..300] of int64;
function qsm(a,b:int64):int64;
var
t,y:int64;
begin
t:=1;
y:=a;
while b<>0 do
begin
if(b and 1)=1 then
t:=(t*y) mod mo;
y:=(y*y) mod mo;
b:=b shr 1;
end;
exit(t);
end;
function c(n,m:int64):int64;
var
i,j,k,l:int64;
begin
if (n=m) or (m=0) or (n=0) then exit(1);
if n>m then
begin
l:=n;
n:=m;
m:=l;
end;
i:=jc[m];
j:=(jc[n]*jc[m-n]) mod mo;
c:=i*qsm(j,mo-2) mod mo;
end;
begin
jc[1]:=1;
for i:=2 to 10000 do
begin
ans:=i;
jc[i]:=(jc[i-1]*ans) mod mo;
end;
mi[0]:=1;
for i:=1 to 100 do mi[i]:=(mi[i-1]*2) mod mo;
zhs[0,0]:=1;
for i:=1 to 10000 do
begin
zhs[i,0]:=1;
for j:=1 to 300 do
begin
zhs[i,j]:=(zhs[i-1,j-1]+zhs[i-1,j]) mod mo;
end;
end;
ans:=0;
readln(n,m,d);
f[0,0]:=1;
ans:=c(m,n);
for i:=0 to 17 do
begin
for j:=0 to n-m do
begin
for k:=0 to n-k do
begin
if (mi[i]*k*(d+1)>n-k) or (k*(d+1)>m div 2) then break
else
begin
f[i+1,j+mi[i]*k*(d+1)]:=(f[i+1,j+mi[i]*k*(d+1)]
+f[i,j]*zhs[m div 2,k*(d+1)]) mod mo;
end;
end;
end;
end;
for i:=0 to n-m do
begin
f[18,i]:=(f[18,i]*zhs[n-m div 2-i,m div 2]) mod mo;
ans:=(ans-f[18,i]+mo) mod mo;
end;
writeln(ans);
end.
end.