题目大意
有m种字母,每种字母可以随便用,让你用这m种字母组成两个长为n的字符串,满足这两个字符串中不含有相同字母的方案数有多少种。
分析
先分类:组成第一个字符串的数目为 i ,对
i 进行分类
不难得出,
ans=∑i=1min(n,m−1)Cim⋅f(n,i)⋅g(n,m−i)f(n,i)表示从由i种字母组成长为n的方案数,i种都要用到g(n,i)表示从由i种字母组成长为n的方案数,i种不一定都要用到
g(n,i) 容易求得 g(n,i)=in
那么现在就是去求 f(n,i) 的问题了分类:考虑长为n的字符串的最后一个字母,它要么是前面已经出现过的,要么没出现过,那么我们可以得到递推式:
f(n,i)=f(n−1,i)⋅i+f(n−1,i−1)⋅i
递推求解f就行了代码
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<map>
#include<algorithm>
#include<set>
#include<stack>
using namespace std;
#define LL long long int
const int MAXN=2008;
const long long int MOD=1000000007;
int T;
LL n,m;
LL c[MAXN][MAXN];
LL f[MAXN][MAXN];//f[i][j]表示长为i由j种字母组成的方案数
LL A(LL k)//k的阶乘
{
LL ans=1;
for(LL i=1;i<=k;i++)ans=(ans*i)%MOD;
return ans;
}
LL G(LL x,LL k)//求x的k次方 mod
{
LL ans=1;
x=x%MOD;
for(;k;k>>=1)
{
if(k&1)ans=(ans*x)%MOD;
x=(x*x)%MOD;
}
return ans;
}
void Make_C()
{
for (int i=0;i<=2002;i++)
for (int j=0;j<=i;j++)
if (!j||i==j)
c[i][j]=1;
else
c[i][j]=(c[i-1][j-1]+c[i-1][j])%MOD;
}
void Make_F()
{
for(int i=1;i<=2000;i++)
{
f[i][1]=1;
for(int j=2;j<=i;j++)
{
f[i][j]=((f[i-1][j]+f[i-1][j-1])%MOD*j)%MOD;
}
}
}
LL Work(LL n,LL m)
{
LL ans=0;
LL max_i=min(n,m-1);
for(LL i=1;i<=max_i;i++)
{
ans=(ans+ ((c[m][i]*f[n][i])%MOD*G(m-i,n))%MOD )%MOD;
}
return ans;
}
int main()
{
LL ans;
Make_C();
Make_F();
scanf("%d",&T);
while(T--)
{
cin>>n>>m;
ans=Work(n,m);
cout<<ans<<endl;
}
}
/*
6
3 2
2 3
2 4
*/