chess(模拟测试69)(DP+组合数)
chess:
题意:
求一个$n*m$的棋盘,使每个$n*n$的区域都有$C$个棋子的方案数。
题解:
从$n==m$的情况入手,设$f[i][j]$为前$i$行放$j$个棋子的方案数,转移为$$f[i][j]=\sum\limits_{k=0}^{min(j,n)}f[i-1][j-k]*C_{n}^{k}$$。
考虑拓展到$m>n$的情况,可以发现第$1$行与第$n+1$行是完全一样的,以此类推,第$i$行与第$n+i$行也是一样的。
所以$DP$柿子可以为$$ f[i][j]=\sum\limits_{k=0}^{min(j,n)}f[i-1][j-k]*(C_{n}^{k})^{\frac{m-i}{n}+1} $$。
然后你会非常高兴的发现你$TLE\ 80$了,因为快速幂复杂度是$O(\log n)$的,所以我们可以预处理出$(C_{n}^{k})^{\frac{m-i}{n}+1}$,然后就可以开心地$AC$了。
code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define R register
#define ll long long
inline ll read()
{
ll aa=0;char cc=getchar();
while(cc<'0'||cc>'9')cc=getchar();
while(cc<='9'&&cc>='0')
aa=(aa<<3)+(aa<<1)+(cc^48),cc=getchar();
return aa;
}
const int N=107,M=10007;
const int mod=1e9+7,md=mod-1;
int n,c;ll m,f[N][M],g[N][N];
ll fac[N],inv[N],finv[N];
inline ll qpow(ll x,ll y)
{
ll res=1;x%=mod;y%=md;
while(y){
if(y&1)res=1ll*res*x%mod;
x=1ll*x*x%mod;y>>=1;
}
return res;
}
void getfac(const int lim)
{
fac[0]=fac[1]=inv[0]=inv[1]=finv[0]=finv[1]=1;
for(R int i=2;i<=lim;++i){
fac[i]=1ll*fac[i-1]*i%mod;
inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
finv[i]=1ll*finv[i-1]*inv[i]%mod;
}
}
inline ll C(int x,int y)
{
if(x<y)return 0;
return 1ll*fac[x]*finv[y]%mod*finv[x-y]%mod;
}
int main()
{
//freopen("chess.in","r",stdin);
//freopen("chess.out","w",stdout);
n=read();m=read();c=read();
getfac(n+3);f[0][0]=1;
for(R int i=1;i<=n;++i)
for(R int j=0;j<=n;++j)
g[i][j]=qpow(C(n,j),(m-i)/n+1);
for(R int i=1;i<=n;++i){
const int lim=i*n;
for(R int j=0;j<=c&&j<=lim;++j)
for(R int k=0;k<=j&&k<=n;++k)
f[i][j]=(f[i][j]+1ll*f[i-1][j-k]*g[i][k]%mod)%mod;
}
printf("%lld",f[n][c]);return 0;
}
/*
2 3 1
*/

浙公网安备 33010602011771号