当前位置: 代码迷 >> 综合 >> [SDOI2011]黑白棋
  详细解决方案

[SDOI2011]黑白棋

热度:50   发布时间:2023-12-12 00:21:07.0

传送门

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

在此前提下,把题目转换为一个 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);
}