Killer Names 2017 多校八;点击打开链接
题意: 给你 m 种字符,求用 m 种字符组成 姓和名 (每个长度为 n ) ,有多少种组合方法?(姓和名中不能有相同的字符)
思路:排列组合;
比赛的时候"成功"的没有推出来,太菜。。。
先说一下比赛时候的思路吧: 其实到现在搜题解的时候发现就跟别人后面求排列的时候有一点不一样,当时想的是怎样求出 x 个字符 全部用上排 n 长度的字符串有多少种,然后就没有找到公式,后来换了一种算出所有的 种数,然后减去重复的部分,奈何样例都过不了。
后来看别人的代码,发现求 x 个字符排 n 长度的字符串可以先算出所有然后去重复,而重复的部分刚好与 x-1 个字符排 n 长度的字符串有关. 其实就是一个递推。
整个思路说一下吧: 给"名" 分 x 个字符,然后排列有 C(m,x)*(m-x)^n*(x^n-C(x,x-1)* f [ x-1 ];
f[ ] 为一个递推公式;
具体看大佬的代码: 点击打开链接
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2005;
const int mod = 1e9+7;
ll c[maxn][maxn],n,m,f[maxn];
ll pow_mod(ll a,ll b)//快速幂
{
ll res = 1;
while(b)
{
if(b&1) res = (res*a)%mod;
a = (a*a)%mod;
b>>=1;
}
return res;
}
void init()//组合数
{
for(int i=0;i<maxn;i++)
{
for(int j=0;j<=i;j++)
{
if(j==0 || j==i) c[i][j]=1;
else c[i][j] = (c[i-1][j-1]+c[i-1][j])%mod;
}
}
}
int main()
{
int t;
init();
scanf("%d",&t);
while(t--)
{
scanf("%lld%lld",&n,&m);
ll ans=0;
for(int i=1;i<=min(n,m);i++)
{
if(i==1)
{
f[1]=1;
ll res = (c[m][1]*pow_mod(m-1,n))%mod;
ans += res;
ans %= mod;
}
else
{
ll res = (c[m][i]*pow_mod(m-i,n))%mod;
ll tmp = 0;
for(int j=1;j<i;j++)//递推求解f[i]
{
tmp += (c[i][j]*f[j])%mod;
tmp %= mod;
}
f[i] = pow_mod(i,n)-tmp;
res *= f[i];
res %= mod;
ans += res;
ans %= mod;
}
}
printf("%lld\n",ans);
}
return 0;
}