自然数幂和&&第一类Stirling数和第二类Stirling数

时间:2019-09-21
本文章向大家介绍自然数幂和&&第一类Stirling数和第二类Stirling数,主要包括自然数幂和&&第一类Stirling数和第二类Stirling数使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

第一类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了,不知道有什么好的实现方法

#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;
}

参考链接:https://blog.csdn.net/doyouseeman/article/details/50826293

原文地址:https://www.cnblogs.com/lfri/p/11561451.html