BZOJ2281: [Sdoi2011]黑白棋

BZOJ2281: [Sdoi2011]黑白棋

https://lydsy.com/JudgeOnline/problem.php?id=2281

分析:

  • \(nimk\)结论,先手必败当且仅当对于每一位有1的石子堆数模(d+1)都等于0。
  • 那么把白棋到黑旗这段看成石子就可以直接用这个结论。
  • \(f[i][j]\)表示考虑前\(i\)位,当前用了\(j\)个石子先手必败的方案数。
  • 转移枚举有多少堆石子这一位是1.

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>
using namespace std;
typedef long long ll;
#define mod 1000000007
#define N 100050
int n,K,d;
ll fac[N],inv[N],f[20][10050];
ll qp(ll x,ll y) {
	ll re=1;
	for(;y;y>>=1,x=x*x%mod) if(y&1) re=re*x%mod; return re;
}
ll C(int x,int y) {
	if(x<y) return 0;
	return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
inline void upd(ll &x,ll y) {
	x=x+y; if(x>=mod) x-=mod;
}
int main() {
	scanf("%d%d%d",&n,&K,&d);
	int i,j,l;
	for(fac[0]=i=1;i<N;i++) fac[i]=fac[i-1]*i%mod;
	inv[N-1]=qp(fac[N-1],mod-2);
	for(i=N-2;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;
	f[0][0]=1;
	for(i=0;i<16;i++) {
		for(j=0;j<=n-K;j++) if(f[i][j]) {
			for(l=0;l*(d+1)<=K/2&&(1<<i)*(d+1)*l+j<=n-K;l++) {
				upd(f[i+1][j+(1<<i)*l*(d+1)],f[i][j]*C(K/2,l*(d+1))%mod);
			}
		}
	}
	ll ans=C(n,K);
	for(i=0;i<=n-K;i++) {
		upd(ans,mod-f[16][i]*C(n-i-K/2,K/2)%mod);
	}
	printf("%lld\n",(ans+mod)%mod);
}
posted @ 2019-01-01 20:14  fcwww  阅读(167)  评论(0编辑  收藏  举报