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 
*/
posted @ 2019-10-17 10:59  Toot_Holmes  阅读(198)  评论(1)    收藏  举报