题意:有m种字符,要求构造两段长度为n的字符串,其中这两段不能有相同的字符
枚举左边选了i种字符,右边可以选1,2....min(n,m-i)种字符
这样就把问题转化为用k种字符构造n长度的字符串的种类有多少种。
第二类stirling数是指将基数为n的集合分为恰好k个(不做区分)非空集合的方法数。这里我们把长度作为基数,把可提供的颜色作为要划分的集合数。由于这里每个集合要唯一区分,所以要乘上k!。之后枚举一下就可以了。
#include <cstdio> #include <iostream> #include <cstring> using namespace std; typedef long long ll; const int mo = 1e9 + 7; long long s[2005][2005],f[2005]; long long myc[2005][2005]; void init() { myc[0][0]=1; myc[1][0]=myc[1][1]=1; for(int i=2;i<=2000;i++) { myc[i][0]=1; myc[i][i] = 1; for(int j=1;j<i;j++) myc[i][j]=(myc[i-1][j]+myc[i-1][j-1])%mo;//组合数 } f[0]=1; for(int i=1;i<=2000;i++) f[i]=(f[i-1]*i)%mo;//阶乘 for(int i=0;i<=2000;i++) s[i][i]=1,s[i][0]=0; for(int i=1;i<=2000;i++) { for(int j=1;j<=i-1;j++) { s[i][j]=(j*s[i-1][j]+s[i-1][j-1])%mo;//斯特林数 } } return ; } int main() { int T,n,m,i,j; cin>>T; init(); while(T--) { cin>>n>>m; long long ans=0; for(i=1;i<=min(m-1,n);i++) { long long now=(((myc[m][i]*s[n][i])%mo)*f[i])%mo; int up=min(n,m-i); for(j=1;j<=up;j++) { ans=(ans+(f[j]*now)%mo*((myc[m-i][j]*s[n][j])%mo))%mo; } } cout<<ans<<endl; } return 0; }