hdu 6143

题目链接
题意:给定m个不同的字符,然后每个人名有姓和名两部分,长度都为n。问这姓名不同且姓和名不能有相同字符的人最多有多少种。
思路:我们可以枚举姓中的字符个数当姓的字符个数定了之后,名的选法就可以选剩下的全部字母了,然后方法有 (mi)n ,当姓不同时,不管名一样不一样,这两个都不是同一个名字。当枚举姓的字符数的时,当字符数为2时,方法不能是 2n 因为这样会有全选1中字符的情况,会有重复,所以要将全为1的减去,但是这个问题就相当于有i种颜色然后给n个不同的小球涂色,有多少种涂法,颜色必须全用。
当i>m答案明显是0,当i==m时答案是i!当 i<m 时可以用递推来求解,ans(i,n)=ans(i,n-1)*i+ans(i-1,n-1)*i;在n个小球上涂i中颜色,就相当于在n-1个小球上涂i中颜色剩下的一个小球随便涂即i中涂法和在n-1个小球上涂i-1中颜色,剩下的一个小球涂剩下的那种颜色但是有i中选法因为有i中颜色所以可以单独挑出来的也就是i种。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=1e6+10;
const LL mod=1e9+7;
LL fac[maxn],inv[maxn];
LL dp[2010][2010];
LL q_mod(LL a,LL k)//快速幂
{
    LL res=1;
    while(k)
    {
        if(k&1)
            res=res*a%mod;
        a=a*a%mod;
        k>>=1;
    }
    return res;
}
LL func(int ball, int color)//根据递推打表
{
    if(dp[ball][color]!=-1) return dp[ball][color];
    LL Count;
    if (ball < color) return 0;
    else if (ball == color)
        return fac[color];
    if(color == 1)
        return 1;
    Count = func(ball - 1, color) * color%mod + func(ball - 1, color - 1) * color%mod;
    return Count%mod;
}
void init()//预处理出 阶乘 逆元 递推表
{
    memset(dp,-1,sizeof(dp));
    fac[0]=1;
    for(int i=1; i<=2000; i++)
        fac[i]=(fac[i-1]*i)%mod;
    inv[2000]=q_mod(fac[2000],mod-2);
    for(int i=1999; i>=1; i--)
        inv[i]=inv[i+1]*(i+1)%mod;
    for(int i=1;i<=2000;i++)
    {
        for(int j=1;j<=i;j++)
        {
            dp[i][j]=func(i,j);
        }
    }
}
LL c_n_m(LL n,LL m)//组合数
{
    if(n<m) return 0;
    if(n==m||m==0) return 1;
    return (inv[m]*inv[n-m]%mod)*fac[n]%mod;
}
int main()
{
    init();
    int ncase;
    scanf("%d",&ncase);
    while(ncase--)
    {
        LL n,m;
        scanf("%lld%lld",&n,&m);
        LL ans=0,tmp=0,ed;
        if(m>=n) ed=n;
        else ed=m-1;
        for(LL i=1; i<=ed; i++)
        {
            LL tmp1;
            tmp1=(c_n_m(m,i)*dp[n][i]%mod);//姓选i中字符的填发有多少种(i中字符要全部用上)
            ans=(ans+tmp1*q_mod(m-i,n)%mod)%mod;//算姓和名的组合有多少种
        }
        printf("%lld\n",ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值