题目描述
dirty 在一个棋盘上放起了棋子。
棋盘规格为 n ∗ m,他希望任意一个 n ∗ n 的区域内都有 K 个棋子。dirty 很快就放置好了一
个满足条件的棋盘方案,但是他认为这样过于简单了,他希望知道有多少个满足条件的方案。
看题第一眼即可知道是关于组合数学的.
通过观察发现,如果棋盘上前n列已经定了,那么之后的情况只会是前面n列的循环.因此只需考虑n列.
可以得出以下式子
sum=Ck1n⋅Ck2n⋅Ck3n⋯Cknnsum=C_{k1}^{n} \cdot C_{k2}^{n} \cdot C_{k3}^{n} \cdots C_{kn}^{n}sum=Ck1n⋅Ck2n⋅Ck3n⋯Cknn
其中K=k1+k2+⋯+knK=k1+k2+\cdots+knK=k1+k2+⋯+kn
这个sumsumsum的含义是在每一列的棋子数固定的情况下前n列的方案数.
那么这种情况对于答案的贡献为
sum⌊mn⌋⋅Ck1n⋅Ck2n⋅Ck3n⋯Ck m mod nnsum^{\lfloor\frac{m}{n}\rfloor}\cdot C_{k1}^{n} \cdot C_{k2}^{n} \cdot C_{k3}^{n} \cdots C_{k\ _{m \ mod\ n}}^{n}sum⌊nm⌋⋅Ck1n⋅Ck2n⋅Ck3n⋯Ck m mod nn
然后将所有的情况利用dp统计.(详见代码)
题目时间限制比较卡,注意预处理出组合数.
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MOD=1e9+7;
const int MAXN=105;
const int MAXM=10005;
int n,c;
ll m,x1,x2;
ll fac[MAXM],finv[MAXM];
ll d[MAXN][MAXM],f[2][MAXN];
void Init(){
fac[0]=1;
for(int i=1;i<MAXM;i++) fac[i]=fac[i-1]*i%MOD;
finv[1]=1;
for(int i=2;i<MAXM;i++) finv[i]=(MOD-MOD/i)*finv[MOD%i]%MOD;
finv[0]=1;
for(int i=1;i<MAXM;i++) finv[i]=finv[i-1]*finv[i]%MOD;
}
ll C(ll x,ll y){
if(x<y) return 0;
return (fac[x]*finv[y]%MOD)*finv[x-y]%MOD;
}
ll ksm(ll x,ll y){
y%=(MOD-1);
ll res=1;
while(y){
if(y&1) res=res*x%MOD;
x=x*x%MOD;
y>>=1ll;
}
return res;
}
int main(){
freopen("chess.in","r",stdin);
freopen("chess.out","w",stdout);
Init();
scanf("%d %lld %d",&n,&m,&c);
if(m<n) return printf("%lld",ksm(2,n*m)),0;
x1=m/n,x2=m%n;
d[n+1][0]=1;
for(int i=0;i<=n;i++) f[0][i]=ksm(C(n,i),x1),f[1][i]=ksm(C(n,i),x1+1);
for(int i=n;i>=1;i--)
for(int j=c;j>=0;j--){
for(int k=0;k<=min(n,j);k++)
d[i][j]=(d[i][j]+d[i+1][j-k]*(i<=x2?f[1][k]:f[0][k])%MOD)%MOD;
if(i==1) break;
}
printf("%lld\n",d[1][c]);
fclose(stdin);
fclose(stdout);
}