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
",(ans+mod)%mod);
}