[CSP-S模拟测试]:chess(数学)
题目描述
$dirty$在一个棋盘上放起了棋子。
棋盘规格为$n\times m$,他希望任意一个$n\times n$的区域内都有$C$个棋子。$dirty$很快就放置好了一个满足条件的棋盘方案,但是他认为这样过于简单了,他希望知道有多少个满足条件的方案。
输入格式
输入三个整数$n,m,C$,含义如题所述。
输出格式
输出一行一个整数,表示答案对$10^9+7$取模的结果。
样例
样例输入:
2 3 1
样例输出:
6
数据范围与提示
对于$20\%$的数据,$n,K\leqslant 4$;
对于另外$20\%$的数据,$m=n$;
对于另外$20\%$的数据,$n\leqslant 50$;
对于$100\%$的数据,$1\leqslant n\leqslant 100$;$1\leqslant m\leqslant 10^{18}$;$1\leqslant C\leqslant n^2$
题解
又没有打正解……
设$dp[i][j]$表示第$i$列放了$j$个的方案数。
$m$很大,显然不能爆扫,所以还要乘上系数,那么式子就变成了:
$$dp[i][j]=\sum \limits_{k=0}^j (C_n^{j-k})^{\frac{m}{n}}\times dp[i-1][k]$$
初值$dp[0][0]=1$。
预处理系数最有时间复杂度为:$\Theta(n\times c^2)$的。
又因为选$c$个和选$n^2-c$个的方案数是一样的,所以我们可以用这种方式优化。
但是$n=m$的点还是需要特判。
时间复杂度:$\Theta(n\times c^2)$。
期望得分:$60$分。
实际得分:$100$分。
代码时刻
#include<bits/stdc++.h>
using namespace std;
const int mod=1000000007;
int n,c;
long long m;
long long C[1001][1001];
long long dp[1001][10001],wzc[1001][10001];
long long jc[10001],inv[10001];
long long qpow(long long x,long long y)
{
long long res=1;
while(y)
{
if(y&1)res=res*x%mod;
x=x*x%mod;
y>>=1;
}
return res;
}
void pre_work()
{
C[0][0]=1;
for(int i=1;i<=n;i++)
{
C[i][0]=1;
for(int j=1;j<=i;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
jc[0]=1;
for(long long i=1;i<=n*n;i++)
jc[i]=(jc[i-1]*i)%mod;
inv[n*n]=qpow(jc[n*n],mod-2);
for(long long i=n*n;i;i--)
inv[i-1]=inv[i]*i%mod;
}
long long get_C(long long x,long long y){return ((jc[x]*inv[y])%mod*inv[x-y])%mod;}
long long lucas(long long x,long long y)
{
if(!y)return 1;
return (get_C(x%mod,y%mod)*lucas(x/mod,y/mod))%mod;
}
int main()
{
scanf("%d%lld%d",&n,&m,&c);
pre_work();
if(n==m)
{
printf("%lld",lucas(n*n,c));
return 0;
}
if(n*n<c*2)c=n*n-c;
for(int i=1;i<=n;i++)
{
long long flag=m/n;
if(i<=m%n)flag++;
flag%=(mod-1);
for(int j=0;j<=c;j++)
dp[i][j]=qpow(C[n][j],flag);
}
wzc[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<=c;j++)
for(int k=0;k<=j;k++)
wzc[i][j]=(wzc[i][j]+wzc[i-1][k]*dp[i][j-k]%mod)%mod;
printf("%lld",wzc[n][c]);
return 0;
}
rp++