hdu 6143第二类striling

题意:有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;  
}  

 

posted @ 2017-10-08 15:12  猪突猛进!!!  阅读(358)  评论(0)    收藏  举报