解析
如果之前有些卡住的题可以说是奇淫技巧的话,这道题的思路只能说太经典了。
感觉其实也就是紫的难度吧,没做出来有些可惜。
还是有写畏黑情绪,见到黑题本能的感觉做不出来。
首先是一个比较自然的题意转化:有 k-1 个 1-n 的球, n 个 0 号球,要求任意前缀 0 号球的数量不少于 1-n 的球的种类数。
然后就不会了
关键就是给每种合法方式找到一种唯一的统计方式。
首先一个常见做法是先钦定一个颜色的出现次序,最后答案乘阶乘即可。
设 fi,jf_{i,j}fi,j 表示放了 i 个白球,j 种球的方案数。
然后,考虑下一个球的颜色,如果是白色,直接填上即可。如果不是白色,我们就同时确定这种颜色剩余的 k-2 个球的位置,这样以后就不用考虑了。由于这些球的位置任意,所以我们刚才的“下一个球”其实是指“下一个空位置的球”。
然后不难发现对于任意合法方案,这样的转移都会且只会统计一次。
时间复杂度 O(n2)O(n^2)O(n2)。
k=1k=1k=1 的时候组合数会出现 -1,直接特判即可。
代码
//luogu
#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_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);#endifn=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;
}