HDU 6143 Killer Names [第二类斯特林数]

题意:给你m个不同的字符,用这些字符组合first name和last name,要求first name和last name中字符完全不相同,求有几种组合。

题解:第二类斯特林数,枚举i(1<=i<=m)个字符填入长度为n的串,我们先枚举 first name 的情况,再枚举last name 的情况 相乘就能得到答案。

定理:第二类Stirling数S(n,k)计数的是把n元素集合划分到k个不可区分的盒子里且没有空盒子的划分个数。

证明:元素在拿些盒子并不重要,唯一重要的是各个盒子里装的是什么,而不管哪个盒子装了什么。

递推公式有:

直接计算S(n,k):



考虑将前p个正整数,1,2,.....p的集合作为要被划分的集合,把{1,2,.....p}分到k个非空且不可区分的盒子的划分有两种情况:
(1)那些使得p自己单独在一个盒子的划分,存在有S(p-1,k-1)种划分个数
(2)那些使得p不单独自己在一个盒子的划分,存在有 k*S(p-1,k)种划分个数

考虑第二种情况,p不单独自己在一个盒子,也就是p和其他元素在一个集合里面,也就是说在没有放p之前,有p-1个元素已经分到了k个非空且不可区分的盒子里面(划分个数为S(p-1,k),那么现在问题是把p放在哪个盒子里面呢,有k种选择,所以存在有k*S(p-1,k)。


AC代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<string.h>
#define mod 1000000007
#define maxn 2005
using namespace std;
typedef long long ll;
ll n,m,C[maxn][maxn],S[maxn][maxn];
void init()
{
    for(ll i=0;i<maxn;i++)
    {
        C[i][0]=1;
        C[i][i]=1;
        for(ll j=1;j<i;j++)
            C[i][j]=(C[i-1][j]%mod+C[i-1][j-1]%mod)%mod;
    }
    S[0][0]=1;
        for(ll i=1;i<maxn;i++)
            for(ll j=1;j<maxn;j++)
                S[i][j]=(S[i][j]+j*S[i-1][j-1]%mod+j*S[i-1][j]%mod)%mod;
}
int main()
{
    init();
    ll T;
    scanf("%lld",&T);
    while(T--)
    {
        scanf("%lld%lld",&n,&m);
        ll ans=0;
        for(ll i=1;i<=min(n,m);i++){
            ll CC=C[m][i],SS=S[n][i],CS=(CC*SS)%mod;
            //printf("%lld %lld %lld\n",i,n,SS);
            ll sum=0;
            for(ll j=1;i+j<=m&&j<=min(n,m);j++){
                sum=(sum+C[m-i][j]*S[n][j]%mod)%mod;
            }
            ans=(ans+sum*CS)%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值