gym-103708E Erudite of words
Erudite of words
组合数学 + 容斥
定义 \(F_i\):表示由 \(i\) 个字母组成的长度为 \(n\) 的单词数(每个字母必须在单词中出现)
显然答案就是 \(F_k * C_{m}^{k}\)
关于 \(F_i\) 的递推式:
\[F_i = i^n - \sum_{j=1}^{k-1}(C_{i}^{j} * F_j)
\]
显然 \(i^n\) 代表 \(i\) 个字母随意摆放的情况,容斥地减去字母数为 \(1,2,3,...,i-1\) 的情况后,剩下的就是 \(F_i\)
大量计算组合数的时候,可以用递推预处理阶乘和阶乘逆元,询问组合数的时候就是 \(O(1)\)
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
const ll mod = 1e9 + 7;
ll fac[maxn], last[maxn], invfac[maxn];
ll qpow(ll x, ll n)
{
ll ans = 1;
while(n)
{
if(n & 1) ans = x * ans % mod;
n >>= 1;
x = x * x % mod;
}
return ans % mod;
}
ll cal(ll up, ll down)
{
ll ans = fac[down] * invfac[up] % mod;
ans = ans * invfac[down - up] % mod;
return ans;
}
int main()
{
ll n, m, k;
cin >> n >> m >> k;
fac[0] = 1;
for(int i=1; i<maxn; i++) fac[i] = fac[i-1] * i % mod;
invfac[maxn - 1] = qpow(fac[maxn - 1], mod - 2);
for(int i=maxn-2; i>=1; i--)
invfac[i] = invfac[i + 1] * (i + 1) % mod;
invfac[0] = invfac[1];
if(k > n) {cout << 0 << endl; return 0;}
for(int i=1; i<=k; i++)
{
last[i] = qpow(i, n);
ll ans = 0;
for(int j=1; j<i; j++)
ans = (ans + cal(j, i) * last[j]) % mod;
last[i] = (last[i] - ans) % mod + mod;
last[i] %= mod;
}
cout << cal(k, m) * last[k] % mod << endl;
return 0;
}