[SDOI2011]黑白棋

传送门

题目中应增加限制:先手不能左移,后手不能右移

在此前提下,把题目转换为一个 n i m nim nim游戏

i i i个白子与第 i i i个黑子中间的格子为一堆石子,每次可以取 d d d

结论:把 n n n堆石子的石子数用二进制表示,统计每位上的 1 1 1的个数,若每位上 1 1 1的个数 m o d    ( d + 1 ) \mod(d+1) mod(d+1)全为0,则必败,否则必胜

f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i K 2 \frac K 2 2K堆石子堆中 j j j颗石子的必败局面数量

枚举前位加起来是 d + 1 d+1 d+1的多少倍 k k k

f [ i + 1 ] [ j + k × ( d + 1 ) × B [ i ] ] + = f [ i ] [ j ] × C k × ( d + 1 ) K 2 ) f[i+1][j+k\times(d+1)\times B[i]]+=f[i][j]\times C^{\frac K 2}_{k\times(d+1)}) f[i+1][j+k×(d+1)×B[i]]+=f[i][j]×Ck×(d+1)2K)

最后用总方案减去必败方案数

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mo=1e9+7;
int n,K,d,p;
ll tt,s,B[25],c[10005][205],f[25][10005];
void pre() {
	for(int i=0; i<=n; ++i) c[i][0]=1;
	for(int i=1; i<=n; ++i) for(int j=1,L=min(2*K,i); j<=L; ++j)
			c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;
}
int C(int n,int m) {
	if(m>n-m) m=n-m;
	return c[n][m];
}
void add(ll &x,ll y) {
	x=(x+y)%mo;
}
int main() {
	B[0]=1;
	for(int i=1; i<=15; ++i) B[i]=B[i-1]<<1;
	scanf("%d%d%d",&n,&K,&d);
	K/=2;
	pre();
	f[0][0]=1;
	for(int i=0; i<15; ++i) for(int j=0; j<=n-2*K; ++j)
			for(int k=0; k*(d+1)<=K&&j+k*(d+1)*B[i]<=n-2*K; ++k)
				add(f[i+1][j+k*(d+1)*B[i]],f[i][j]*C(K,k*(d+1)));
	for(int i=0; i<=n-2*K; ++i) add(s,f[15][i]*C(n-i-K,K));
	tt=C(n,K*2);
	return !printf("%d",(tt-s+mo)%mo);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值