您的位置:首页 > 大数据 > 人工智能

2017 Multi-University Training Contest - Team 8:Killer Names(排列组合+容斥)

2017-08-18 12:43 387 查看
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6143

题目意思给出m个字母,然后编排名字,名字由姓和名组成,姓和名的长度均为n。

给出m个不同字符,求能编排多少名字,姓和名中不能有相同的字符,而且姓名不能

有重复的,最后答案模1e9+7。

当我们从m个字符中选出i个来组成姓的时候,剩余的m-i个字符可以用来组成名字。

从m个字符重选择i个字符的选择字符的方案数有:C(m,i)

对于选出的i个字符,我们在每个位置上都可以放置这i个字符,总共有i^n种方法,但是

这样有很多重复的,我们需要减去这些重复的,我们要的是恰好由i个字符组成的姓。

因此我们用i^n减去恰好i-1个字符组成姓的方案,减去恰好i-2个字符组成姓的方案.......直到恰好1个

字符组成姓的方案,最后得到的是恰好i个字符组成姓的方案,再成上挑选这i个字符的方案。

对于名来说,n个位置,每个位置放置的字符都有m-i种选择,则对应的名字的组合方案就是(m-i)^n

种。

总结公式如下:

说明C[i,j]是从i个数里面挑j个的组合数。

a[j]是恰好j个字符组成姓的方案数。



AC代码:

#include <iostream>
#include <stdio.h>
#include <string.h>

using namespace std;

typedef long long LL;
const int maxn = 2002;
const int mod = 1e9+7;
LL C[maxn][maxn];
LL a[maxn]; ///a[i]代表恰好i个字符去组成姓的方案数。
///打表求组合数
void getCombination()
{
memset(C,0,sizeof(C));
C[1][0] = 1;
C[1][1] = 1;
for(int i = 2; i < maxn; i++)
{
C[i][0] = 1;
for(int j = 1; j <= i+1; j++)
C[i][j] = (C[i-1][j-1]+C[i-1][j])%mod;
}
}
///整数快速幂取模
LL Integer_QuickPow(LL n,LL k)
{
LL ans,res;
ans = 1;
res = n;
while(k)
{
if(k&1)
ans = (ans*res)%mod;
res = (res*res)%mod;
k = k>>1;
}
return ans;
}
///去重
LL Calculate_Sum(LL k,LL n)
{
LL powerk = Integer_QuickPow(k,n); ///求k的n次方
LL sum = 0;
for(int j = 1; j < k; j++)
{
sum = (sum+(C[k][j]*a[j])%mod)%mod;
}
LL ans = ((powerk-sum)%mod+mod)%mod;
return ans;
}
///求解
LL solve(LL n,LL m)
{
LL ans = 0;
a[0] = 0;
for(int i = 1; i < m; i++)
{
a[i] = Calculate_Sum(i,n);
LL power = Integer_QuickPow(m-i,n);
ans = (ans+((C[m][i]*a[i]%mod)*power)%mod)%mod;
}
return ans;
}
int main()
{
LL n,m;
getCombination();
int t;
cin>>t;
while(t--)
{
cin>>n>>m;
cout<<solve(n,m)<<endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐