解析
如果之前有些卡住的题可以说是奇淫技巧的话,这道题的思路只能说太经典了。 感觉其实也就是紫的难度吧,没做出来有些可惜。 还是有写畏黑情绪,见到黑题本能的感觉做不出来。
首先是一个比较自然的题意转化:有 k-1 个 1-n 的球, k 个 0 号球,要求任意前缀 0 号球的数量不少于 1-n 的球的种类数。 然后就不会了 关键就是给每种合法方式找到一种唯一的统计方式。
首先一个常见做法是先钦定一个颜色的出现次序,最后答案乘阶乘即可。 设
f
i
,
j
f_{i,j}
fi,j? 表示放了 i 个白球,j 种球的方案数。 然后,考虑下一个球的颜色,如果是白色,直接填上即可。如果不是白色,我们就同时确定这种颜色剩余的 k-2 个球的位置,这样以后就不用考虑了。由于这些球的位置任意,所以我们刚才的“下一个球”其实是指“下一个空位置的球”。 然后不难发现对于任意合法方案,这样的转移都会且只会统计一次。
时间复杂度
O
(
n
2
)
O(n^2)
O(n2)。
k
=
1
k=1
k=1 的时候组合数会出现 -1,直接特判即可。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define LL __int128
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
using namespace std;
const int N=2050;
const int mod=1e9+7;
inline ll read(){
ll x(0),f(1);char c=getchar();
while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}
while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
inline ll ksm(ll x,ll k){
ll res(1);
while(k){
if(k&1) res=res*x%mod;
x=x*x%mod;
k>>=1;
}
return res;
}
int n,k;
ll jc[N*N],ni[N*N];
void init(int n){
jc[0]=1;
for(int i=1;i<=n;i++) jc[i]=jc[i-1]*i%mod;
ni[n]=ksm(jc[n],mod-2);
for(int i=n-1;i>=0;i--) ni[i]=ni[i+1]*(i+1)%mod;
return;
}
inline ll C(int n,int m){
return jc[n]*ni[m]%mod*ni[n-m]%mod;
}
ll f[N][N];
signed main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
n=read();k=read();
if(k==1){
puts("1");return 0;
}
init(n*k);
for(int i=1;i<=n;i++){
f[i][0]=1;
for(int j=1;j<=i;j++){
f[i][j]=(f[i-1][j]+f[i][j-1]*C(n*k-i-(j-1)*(k-1)-1,k-2))%mod;
}
}
printf("%lld\n",f[n][n]*jc[n]%mod);
return 0;
}
|