AGC002 F Leftmost Ball——DP
题目:https://atcoder.jp/contests/agc002/tasks/agc002_f
充要条件是前缀0的个数 >= 颜色种数。
设计 DP ,放一个颜色的时候就把所有该颜色的点都考虑完,不要一个一个放。这样就不用考虑 “剩下多少个旧颜色的点可用” 了。
新放一种颜色的时候,知道现在已经填了多少个位置,所以所有该颜色点的放置方案数是可算的。
dp[ i ][ j ] 表示放了 i 个 0 、j 种颜色的方案。认为颜色是按顺序放的,最后乘上阶乘。就有 \( dp[i][j] -> dp[i+1][j] , dp[i][j]*\binom{(n-j)(k-1)+n-i-1}{k-2} -> dp[i][j+1] \) 。
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int N=2005,M=N*N,mod=1e9+7; int upt(int x) {while(x>=mod)x-=mod;while(x<0)x+=mod;return x;} int pw(int x,int k) {int ret=1;while(k){if(k&1)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=1;}return ret;} int n,k,jc[M],jcn[M],dp[N][N]; void init() { int lm=n*k; jc[0]=1;for(int i=1;i<=lm;i++)jc[i]=(ll)jc[i-1]*i%mod; jcn[lm]=pw(jc[lm],mod-2); for(int i=lm-1;i>=0;i--)jcn[i]=(ll)jcn[i+1]*(i+1)%mod; } int C(int n,int m) { if(n<0||m<0||n<m)return 0; return (ll)jc[n]*jcn[m]%mod*jcn[n-m]%mod; } int main() { scanf("%d%d",&n,&k); if(k==1){puts("1");return 0;} init(); dp[0][0]=1; for(int i=0;i<=n;i++) for(int j=0;j<=i;j++) { if(!dp[i][j])continue;int tp=dp[i][j]; if(i<n)dp[i+1][j]=upt(dp[i+1][j]+tp); if(j<i) { int ml=C((n-j)*(k-1)+n-i-1,k-2); dp[i][j+1]=(dp[i][j+1]+(ll)ml*tp)%mod; } } printf("%lld\n",(ll)dp[n][n]*jc[n]%mod); return 0; }