题解 AT2000 【[AGC002F] Leftmost Ball】
\[\huge\mathbb{DESCRIPTION}
\]
编号:\(AT2000\)
算法:乘法逆元、\(\mathcal{DP}\)
时间复杂度:\(\mathcal O(N^2)\)
\[\huge\mathbb{SOL}
\]
我们直接来推式子吧...
首先如果\(K=1\),就直接输出1。
设\(Dp[i][j]\)表示用了\(i\)个白球和\(j\)个彩色球的合法的方案数量。
首先,如果你已经放了\(i-1\)个白球和\(j\)个彩球,这时你再在后面放一个白球肯定不影响。
因此,\(Dp[i][j]\)可以由\(Dp[i-1][j]\)转移过来。
其次,\(Dp[i][j]\)也可以由\(Dp[i][j-1]\)转移而来。
我们看看为什么吧...
因为已经选了\(j-1\)个彩球,所以还有\(N-j+1\)个彩球没有选。
如果我们选了某一种颜色的球,我们就必须从这种颜色的\(K\)个彩球里选一个放上。
此时,这种颜色的彩球减少了\(2\)个,一个被涂成了白色,一个没有变色。
所以还剩\(K-2\)个这种彩球。
所以我们需要在剩下的\(N*K-i-1(j-1)*(K-1)\)个位子中选\(K-2\)个位子。
所以我们就得出了状态转移方程:
\[Dp[i][j]=Dp[i-1][j]+Dp[i][j-1]*(N-j+1)*C_{N*K-i-1(j-1)*(K-1)}^{K-2}
\]
转移前求一下阶乘和逆元即可。
\[\huge\mathbb{CODE}
\]
#include<bits/stdc++.h>
#define MOD 1000000007
#define MAX 2001
using namespace std;
long long N,K;
long long Inv[MAX*MAX];
long long Dp[MAX][MAX];
long long Factorial[MAX*MAX];
inline long long Quick_Power(long long X,int P)
{
register long long Return;
Return=1;
while(P)
{
if(P&1)
{
Return=Return*X%MOD;
}
X=X*X%MOD;
P>>=1;
}
return Return;
}
inline long long Combination(long long A,long long B)
{
return Factorial[A]*Inv[B]%MOD*Inv[A-B]%MOD;
}
int main(void)
{
register long long i,j;
cin>>N>>K;
if(K==1)
{
cout<<1<<endl;
return 0;
}
Factorial[0]=1;
for(i=1;i<=N*K;i++)
{
Factorial[i]=Factorial[i-1]*i%MOD;
}
Inv[N*K]=Quick_Power(Factorial[N*K],MOD-2);
for(i=N*K-1;i>=0;i--)
{
Inv[i]=Inv[i+1]*(i+1)%MOD;
}
Dp[0][0]=1;
for(i=1;i<=N;i++)
{
for(j=0;j<=i;j++)
{
if(!j)
{
Dp[i][j]=Dp[i-1][j];
continue;
}
Dp[i][j]=Dp[i-1][j]+Dp[i][j-1]*(N-j+1)%MOD*Combination(N*K-i-1-(j-1)*(K-1),K-2)%MOD;
}
}
cout<<Dp[N][N]<<endl;
return 0;
}
不要妄图追上西坠的太阳,而是要在黎明前就等着它!