第一类Stirling数
首先设
$$S_k(n)=\sum_{i=0}^ni^k$$
根据第一类斯特林数的定义(P是排列数,C是组合数,s是Stirling)
$$C_n^k={P_n^k\over k!}={\sum_{i=0}^k(-1)^{i+k}s(k,i)n^i\over k!}$$
变形得
$$ n^k ={\sum_{i=0}^{k-1}(-1)^{i+k}s(k,i)n^i}-k! C_n^k$$
$n$ 从1取到n累加,
$$S_k(n)=\sum_{j=0}^n(k!C_j^k-\sum_{i=0}^{k-1}(-1)^{i+k}s(k,i)j^i)$$
拆括号
$$=k!\sum_{j=0}^nC_j^k-\sum_{i=0}^{k-1}(-1)^{i+k}s(k,i)\sum_{j=0}^nj^i$$
因为 $ C_{m+1}^{n+1}=C_m^n+C_{m}^{n+1} $,可推出 $\sum_{i=0}^nC_i^k=C_{n+1}^{k+1}$,
在转换为用排列数的
$$S_n(k)={P_{n+1}^{k+1}\over k+1}-\sum_{i=0}^{k-1}(-1)^{i+k}s(k,i)S_i(n)$$
那么我们只需要用 $O(k^2)$ 地预处理出第一类斯特林数,然后按k来递推了,边界是 $S_1(n)=n(n+1)/2$
主要运用了第一类斯特林数与排列式P的关系。
优点是可以避开除法,不用考虑模数有没有逆元,排列数的形式一定可以整除($k+1$ 个连续值相乘,其中肯定有 $k+1$ 的倍数)
//单次查询是 $O(k^2)$,多组测试就gg了,不知道有什么好的实现方法
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll mod = 1e9 + 7; const int maxk = 2000 + 1; ll n, k; ll stir[maxk][maxk]; void init() { stir[0][0] = stir[1][1] = 1; for(int i = 2;i < maxk;i++) for(int j = 1;j <= i;j++) stir[i][j] = (stir[i-1][j-1] + (i-1)*stir[i-1][j]) % mod; } ll S[maxk]; //S[i]表示前n项的i次方之和 ll cal() { n %= mod; S[1] = (n+1) * n / 2 % mod; //假设 k>=1 for(int i = 2;i <= k;i++) { //计算前面一坨 ll prod; if(i > n) prod = 0; else { ll kk = i+1; prod = 1; for(ll j = 0;j <= i;j++) { ll tmp = n+1-j; if(tmp % (i+1) == 0) prod = prod * (tmp/(i+1)) % mod; else prod = prod * tmp % mod; } } ll tmp = 0, sig; for(int j = 0;j < i;j++) { sig = (j+i)&1 ? -1 : 1; tmp = (tmp + sig*stir[i][j]*S[j]%mod + mod) % mod; } S[i] = ((prod - tmp)%mod + mod) % mod; } return S[k]; } int main() { init(); int T; scanf("%d", &T); while(T--) { scanf("%lld%lld", &n, &k); printf("%lld\n", cal()); } return 0; }
第二类Stirling数
首先需要证明一个式子
$$i^k = \sum_{j=1}^kS(k, j)*C_i^j*j!$$
证:对于一个 $i^k$,可以具体理解为把 $k$ 个不同的球放入 $i$ 个不同盒子里的方案数(允许空盒);
现在,枚举恰好有 $j$ 个盒子放有球,$j$ 可从1取到 $k$,所以方案数为 $\sum_{j=1}^kS(k, j)*C_i^j*j!$;
证毕。
$\sum \limits ^{n}_{i=0} i^k = \sum \limits _{i=0}^{n}\sum\limits _{j=1}^{k} S_{k,j}*C_{i,j}*j!$
然后讨论 $S(k, j)$ 的系数和:$\sum \limits ^{n}_{i=0} C_{i,j} *j!$,即 $j!* \sum\limits^{n}_{i=0} C_{i,j}$
已知:$\sum \limits_{i=0}^{n}C_{i,j}=C_{n+1,j+1}$
所以 $S(k, j)$ 的系数为 $j!*C_{n+1}^{j+1}$,
于是 $\sum \limits ^{n}_{i=0} i^k= \sum\limits_{j=1}^{k}S(k, j)*j!*C_{n+1}^{j+1}$.
变成排列数形式:$\displaystyle \sum \limits ^{n}_{i=0} i^k= \sum\limits_{i=1}^{k}S(k, i)* \frac{P_{n+1}^{i+1}}{{i+1}}$
然后预处理斯特林数就可以解决了.
//同样单次查询是 $O(k^2)$
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll mod = 1e9 + 7; const int maxk = 2000 + 1; ll n, k; ll stir[maxk][maxk]; void init() { stir[0][0] = stir[1][1] = 1; for(int i = 2;i < maxk;i++) for(int j = 1;j <= i;j++) stir[i][j] = (stir[i-1][j-1] + j*stir[i-1][j]) % mod; } ll cal() { n %= mod; ll sum = 0; for(int i = 1;i <= k;i++) { ll prod; if(i > n) prod = 0; else { prod = 1; for(int j = 0;j <= i;j++) { ll tmp = n+1-j; if(tmp % (i+1) == 0) tmp /= (i+1); prod = prod * tmp % mod; } } //printf("%lld\n", prod); sum = (sum + stir[k][i]*prod) % mod; } return sum; } int main() { init(); int T; scanf("%d", &T); while(T--) { scanf("%lld%lld", &n, &k); printf("%lld\n", cal()); } return 0; }
参考链接: