BZOJ2281 [SDOI2011]黑白棋 【dp + 组合数】

题目

小A和小B又想到了一个新的游戏。
这个游戏是在一个1*n的棋盘上进行的,棋盘上有k个棋子,一半是黑色,一半是白色。
最左边是白色棋子,最右边是黑色棋子,相邻的棋子颜色不同。
小A可以移动白色棋子,小B可以移动黑色的棋子,他们每次操作可以移动1到d个棋子。
每当移动某一个棋子时,这个棋子不能跨越两边的棋子,当然也不可以出界。当谁不可以操作时,谁就失败了。
小A和小B轮流操作,现在小A先移动,有多少种初始棋子的布局会使他胜利呢?

输入格式

共一行,三个数,n,k,d。

输出格式

输出小A胜利的方案总数。答案对1000000007取模。

输入样例

10 4 2

输出样例

182

提示

1<=d<=k<=n<=10000, k为偶数,k<=100。

题解

同BZOJ3576小奇的博弈

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
using namespace std;
const int maxn = 10005,maxm = 100005,INF = 1000000000,P = 1000000007;
inline int read(){
	int out = 0,flag = 1; char c = getchar();
	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
	return out * flag;
}
LL f[17][maxn],C[maxn][105];
int n,K,d;
void init(){
	for (int i = 0; i <= n; i++){
		C[i][0] = 1;
		for (int j = 1; j <= i && j <= K; j++)
			C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % P;
	}
	
}
int main(){
	n = read(); K = read(); d = read();
	init();
	f[0][0] = 1;
	for (LL i = 0; i <= 16; i++){
		for (LL j = 0; j <= n - K; j++){
			for (LL x = 0; x * (d + 1) <= K / 2 && x * (d + 1) * (1ll << i) + j <= n - K; x++)
				f[i + 1][j + x * (d + 1) * (1ll << i)] = (f[i + 1][j + x * (d + 1) * (1ll << i)] + f[i][j] * C[K / 2][x * (d + 1)] % P) % P;
		}
	}
	LL ans = 0;
	for (LL i = 0; i <= n - K; i++) ans = (ans + f[16][i] * C[n - K - i + K / 2][K / 2]) % P;
	printf("%lld
",((C[n][K] - ans) % P + P) % P);
	return 0;
}

原文地址:https://www.cnblogs.com/Mychael/p/8575554.html