AT2000 [AGC002F] Leftmost Ball
首先我们发现,直接计算的方案数一定是有重复的,这启发我们添加限制去重。
我们钦定第\(i\)个白球原始颜色就是第\(i\)次出现的颜色。这样每种序列就会被对应到一种初始颜色。
然后考虑对这个初始颜色序列计数。设\(dp_{i,j}\)表示放了第\(i\)个颜色,还有\(j\)个颜色的后\(k-1\)个球没有处理。
每次转移可以放新的一个颜色的球,或者将当前第一个颜色的后\(k-1\)个球处理掉。
预处理阶乘转移即可,时间复杂度\(O(nk)\)
code:
#include<bits/stdc++.h>
#define I inline
#define ll long long
#define db double
#define lb long db
#define N (2000+5)
#define M ((N<<1)+5)
#define K (1500+5)
#define mod 1000000007
#define Mod (mod-1)
#define eps (1e-5)
#define ull unsigned ll
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((k+1)*(x)+(y))
#define R(n) (1ll*rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;
int n,k,Ne,La;ll dp[2][N],frc[N*N],Inv[N*N];
I ll mpow(ll x,int y=mod-2){ll Ans=1;while(y) y&1&&(Ans=Ans*x%mod),y>>=1,x=x*x%mod;return Ans;}
I ll C(int x,int y){return frc[x]*Inv[y]%mod*Inv[x-y]%mod;}
int main(){
freopen("1.in","r",stdin);
int i,j;scanf("%d%d",&n,&k);if(k==1){puts("1");return 0;}for(frc[0]=i=1;i<=n*k;i++) frc[i]=frc[i-1]*i%mod;Inv[n*k]=mpow(frc[n*k]);for(i=n*k-1;~i;i--) Inv[i]=Inv[i+1]*(i+1)%mod;
Ne=0;La=1;dp[0][0]=1;for(i=1;i<=2*n;i++){
Ne^=1;La^=1;Me(dp[Ne],0);for(j=1;j<=n;j++) dp[Ne][j]=dp[La][j-1];
for(j=0;j<n;j++) dp[La][j+1]&&(dp[Ne][j]=(dp[Ne][j]+dp[La][j+1]*C(k*(n-(i-j-2)/2)-j-2,k-2))%mod);
}printf("%lld\n",dp[Ne][0]*frc[n]%mod);
}