P2490[SDOI2011]黑白棋【博弈论,dp】

正题

题目链接:https://www.luogu.com.cn/problem/P2490


题目大意

一个长度为\(n\)的棋盘上放下\(k\)个棋子。

第一个要是白色,下一个要是黑色,在下一个是白色以此类推。

先手操控白,后手操控黑。白色只能往右,黑色只能往左。每次操作的可以移动\(d\)个棋子任意步。

求先手必胜的初始状态数

\(1\leq d\leq k\leq n\leq 10^4,1\leq k\leq 100\)\(k\)为偶数


解题思路

把两个黑白棋子之间的长度看为石头堆就是一个\(Nim_k\)游戏了。

\(Nim_k\)游戏的结论就是\(k+1\)进制下各个位置的\(1\)的个数\(\% (k+1)\)等于\(0\)的话先手必败。

因为先手必胜比较麻烦,考虑减去先手必败的情况

这个东西和昨天的一道\(ARC\)的题目很像,每个位分开考虑,设\(f_{i,j}\)表示前\(i\)个位都是\(0\)时,用了\(j\)个石头的方案。

那么转移也十分显然,枚举一个选的倍数\(i\)然后分配到\(\frac{k}{2}\)个石头堆中,方案数就是\(\binom{\frac{k}{2}}{i\times (d+1)}\)

然后统计答案的时候对于石子和为\(i\)的贡献就是\(\binom{n-\frac{k}{2}-i}{\frac{k}{2}}\)(因为每一堆的个数固定,所以选择起点就好了)

时间复杂度\(O(nk\log n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1e4+10,M=110,P=1e9+7;
ll n,k,d,ans,C[N][M],f[16][N];
signed main()
{
	scanf("%lld%lld%lld",&n,&k,&d);
	C[0][0]=1;n-=k;k/=2;d++;
	for(ll i=1;i<N;i++)
		for(ll j=0;j<M;j++)
			C[i][j]=((j?C[i-1][j-1]:0)+C[i-1][j])%P;
	ll z=0;ans=C[n+2*k][k*2];f[0][0]=1;
	for(ll p=1;p<=n;p<<=1){
		z++;
		for(ll j=0;j<=n;j++)
			for(ll i=0;j+i*p*d<=n&&i*d<=k;i++)
				(f[z][j+i*p*d]+=f[z-1][j]*C[k][i*d]%P)%=P;
	}
	for(ll i=0;i<=n;i++)
		(ans+=P-f[z][i]*C[n+k-i][k]%P)%=P;
	printf("%lld\n",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/QuantAsk/p/14593710.html