51nod 1829 函数
想知道f:A->B这个函数(其中|A|=n, |B|=m)的所有映射关系要使B的每个元素都要被A的一个元素覆盖到。
数字可能很大你只要输出方案数模1,000,000,007即可。1≤n,m≤1,000,000
转换一下题意:给定n个有区别的小球,装到m个有区别的箱子里,且没有箱子是空的,求方案数
我们知道第二类斯特林数 S(n,m) 表示 n 个有区别的小球装到 m 个无区别的箱子里且没有箱子是空的的方案数,那么 m! × S(n,m) 即为答案(因为对于箱子进行全排列后就是有区别的了)
根据第二类斯特林数的定义,对于计算 S(n,m),第 n 个小球只有两种选法:1. 独自放在一个新箱子中 2. 放到之前的某个箱子里
这样做的话就可以保证没有箱子是空的,递归计算的话是:S(n,m)=S(n-1,m-1)+m×S(n-1,m)
但这样计算的时间复杂度为 O(nm),对于本题是无法通过的
由于只需要计算 S(n,m),所以可以用第二类斯特林数的意义进行容斥
如果不要求箱子有空的话,根据乘法原理,每个求有 m 种选择方案,则答案就是 mn
但是有非空的限制,所以可以枚举有多少不是空的,然后剩下的随便填
即 $S(n,m)=\frac{\sum\limits_{k=0}^{m}(-1)^kC(m,k)(m-k)^n}{m!}$
于是就得到了可以在 O(m ln n) 内计算的算法
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 1e6 + 10, p = 1e9 + 7; 4 int fac[N], inv[N], n, m; 5 int pw(int a, int b) { 6 int r = 1; 7 for( ; b ; b >>= 1, a = 1ll * a * a % p) if(b & 1) r = 1ll * r * a % p; 8 return r; 9 } 10 int C(int n, int m) { 11 return 1ll * fac[n] * inv[m] % p * inv[n - m] % p; 12 } 13 int main() { 14 scanf("%d%d", &n, &m); 15 inv[0] = fac[0] = 1; 16 for(int i = 1 ; i <= N - 10 ; ++ i) fac[i] = 1ll * fac[i - 1] * i % p, inv[i] = pw(fac[i], p - 2); 17 int ans = 0, sig = 1; 18 for(int k = 0 ; k <= m ; ++ k) { 19 ans = (1ll * ans + 1ll * sig * C(m, k) * pw(m - k, n) % p) % p; 20 sig = - sig; 21 } 22 printf("%d\n", (1ll * ans % p + p) % p); 23 }
$$S(n,m)=\frac{1}{m!}\sum_{k=0}^m(-1)^k\frac{m!}{k!(m-k)!}(m-k)^n=\sum_{k=0}^{m}\frac{(-1)^k}{k!}\frac{(m-k)^n}{(m-k)!}$$
设$h(m)=\sum\limits_{i=0}^{m}\frac{(-1)^i}{i!}\frac{(m-i)^n}{(m-i)!}=\sum\limits_{i=0}^{m}f(i)g(m-i)$,这就是一个卷积形式了,可以用FFT/NTT优化计算到 $O(m \ln m)$
斯特林数反演
$$f(n)=\sum_{i=1}^{n}S(n,i)g(i) \Leftrightarrow g(n)=\sum_{i=1}^{n}(-1)^{n-i}s(n,i)f(i)$$
$$n^k=\sum_{i=0}^{k}S(k,i)C(n,i)i!=\sum_{i=1}^{k}S(k,i)n^\underline i$$
常用于各种奇奇怪怪的有次幂的计数问题
很好理解,左边就是$k$个球可以任意放置在$n$个盒子里。
右边就是枚举非空盒子的数量$i$,那么把$k$个球放在$i$个盒子(盒子不同,需要乘上一个$i!$)里面再乘上选出$i$个非空盒子的方案数。
有了这个东西,我们可以很方便的维护一些东西