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);
}

猜你喜欢

转载自www.cnblogs.com/suika/p/10205824.html
今日推荐