题意:m个字符组成2个长度分别为n的名和字,要求名和字不能有相同的字符,有多少可能
1.容斥原理:对于名我们选i个字符(这i个字符都要用到);对于字我们就选剩下的所有字符(任意选取其中一些字符使用);对于字的种数使用快速幂模,对于名我们定义f函数,f[i]表示只使用i种字母的情况,f[i]=i^n-f[i-1]*C(i,i-1)-f[i-2]*C(i, i-2)…-f[1]*C(i,1);
因此最终的结果便是
∑
i
=
1
m
−
1
C
(
m
,
i
)
∗
f
[
i
]
∗
(
m
−
i
)
n
\sum_{i=1}^{m-1}C(m,i)*f[i]*(m-i)^n
∑i=1m−1C(m,i)∗f[i]∗(m−i)n
代码:
#include<bits/stdc++.h>
#include<cstdio>
const int mod=1e9+7;
typedef long long ll;
ll C[2006][2006];
ll f[2005];
using namespace std;
void Cnm()
{
int len=2005;
C[1][1]=1;
for(int i=2; i<len; i++)
{
C[i][1]=i;
for(int j=2 ; j<=i; j++)
{
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
}
}
ll quick_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 fi(ll n)
{
f[1]=1;
for(int i=2; i<=n; i++)
{
ll temp=0;
for(int j=1; j<i; j++)
{
temp=(temp+C[i][j]*f[j]%mod)%mod;
}
f[i]=(quick_pow_mod(i, n)+mod-temp)%mod;
}
}
int main()
{
Cnm();
int T;
scanf("%d", &T);
while(T--)
{
ll n,m,ans;
scanf("%lld%lld", &n,&m);
fi(n);
if(n<m)
{
ans=0;
for(int i=1; i<=n; i++)
{
ans=(ans+C[m][i]*f[i]%mod*quick_pow_mod(m-i,n)%mod)%mod;
}
}
else
{
ans=0;
for(int i=1; i<=m-1; i++)
{
ans=(ans+C[m][i]*f[i]%mod*quick_pow_mod(m-i, n)%mod)%mod;
}
}
printf("%lld\n", ans);
}
return 0;
}
思路2:dp,dp[i][j]表示长度为i使用了j种字符的可能,递推方程:
dp[i][j]=dp[i-1][j-1]*(m-j+1)+dp[i-1][j]*j
代码:
#include<bits/stdc++.h>
#include<cstdio>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
ll dp[2005][2005];
ll quick_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;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
int n,m;
ll ans=0;
scanf("%d%d", &n, &m);
dp[1][1]=m;
for(int i=2; i<=n; i++)
{
for(int j=1; j<=i && j<m; j++)
{
dp[i][j]=(dp[i-1][j]*j%mod+dp[i-1][j-1]*(m-j+1)%mod)%mod;
}
}
for(int i=1; i<m; i++)
{
ans=(ans+dp[n][i]*quick_mod(m-i,n)%mod)%mod;
}
printf("%lld\n", ans);
}
return 0;
}