题意
给你n种颜色的球,编号为1到n,每种k个,然后把这些球放到一个序列上,每种颜色的最左边的球变成0,然后问有多少种序列?
1<=n,k<=2000
分析
首先考虑合法的满足什么性质,正着来的话,一定是0的数量大于等于颜色的数量,这个还满足充分性
我们还可以发现一个计数的技巧,就是我们可以把这些序号统一一下,最左边的i肯定放在最左边的i+1的左边,这样我们再乘上n!即可
然后我们来考虑正着dp,因为我们很难维护这个序列每个位置是长什么样子的,考虑不维护具体的位置,而是维护每种颜色的球
考虑 dp[i][j] d p [ i ] [ j ] 表示放了i个0的球,放了前j个颜色的方案数,但是这样的话你不知道你的颜色放在哪里比较好,不能保证最左边的i放在最左边的i+1的左边,插进去的时候且不能保证放在0的后面
考虑反着dp,那么就是0的数量小于等于颜色的数量
那么你放0的时候就可以保证颜色都在后面
放颜色的时候你只要保证留一个颜色在当前位,剩下的在后面随便插就好了,保证了编号大的颜色在后面
插入的时候就是一个可重复组合
具体是
dp[i][j]=dp[i−1][j]+dp[i][j−1](((j−1)k+i+1)+(k−1)−1k−1)
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
]
+
d
p
[
i
]
[
j
−
1
]
(
(
(
j
−
1
)
k
+
i
+
1
)
+
(
k
−
1
)
−
1
k
−
1
)
代码
#include <bits/stdc++.h>
#define ll long long
#define C(i,j) (fac[i] * inv[j] % Mod * inv[(i)-(j)] % Mod)
using namespace std;
const ll N = 10000010;
const ll Mod = 1e9+7;
inline ll read()
{
ll p=0; ll f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
ll n,k; ll fac[N],inv[N];
ll f[2010][2010];
void upd(ll &x,ll y){x=(x+y)%Mod;}
int main()
{
n = read(); k = read();
fac[0] = 1; for(ll i=1;i<=n*k+n+k;i++) fac[i] = i;
inv[0] = inv[1] = 1; for(ll i=2;i<=n*k+n+k;i++) inv[i] = (Mod - Mod / i) * inv[Mod%i] % Mod;
for(ll i=1;i<=n*k+n+k;i++) fac[i] = fac[i-1] * i % Mod,inv[i] = inv[i-1] * inv[i] % Mod;
f[0][0] = 1; k--; if(!k){printf("1\n"); return 0;}
for(ll i=0;i<=n;i++)
{
for(ll j=i;j<=n;j++)
{
if(i) upd(f[i][j] , f[i-1][j]);
if(j) upd(f[i][j] , f[i][j-1] * C(j*k+i-1,k-1) % Mod);
}
}
return printf("%lld\n",f[n][n] * fac[n] % Mod),0;
}