高效求解自然数k次幂和的方法

本文介绍了如何高效地求解自然数k次幂和的问题,探讨了快速幂、拉格朗日插值法、牛顿插值法、伯努利数和矩阵乘法等方法,重点讲解了利用第一类斯特林数进行计算,并给出了代码实现。

前言

寒假集训比赛时遇到了一道《WYF的盒子》,题目大意就是求 ni=mik p 取模的结果。
其中有的数据点满足nm5000 k1012 ,剩下的数据点满足 n,m1012 k2000 。对于所有数据模数 p1012
其中 n m相差很小的点显然可以使用快速幂,但是剩下的点怎么做?换言之,我们需要找到一个时间复杂度之和 k 有关的高效的求自然数幂和的算法。


普通公式

运用二项式定理我们可以推出一般的公式。

Sk=1k+1[(n+1)k+1(n+1)i=1k1Ckik+1Si]

推算过程链接:http://wenku.baidu.com/link?url=F07Cj1SxmyNr8_EriB3UrJU8tn4f3aByvpYKPvrLCoqV2HWjeXG1y6XWbgFQL8mBcP5Z0is1Sqpe0zxbDRmMSsBjt-0mQRRvvHTMqq57gpK
当然我们也可以不使用二项式定理,只研究递推关系,也可以得到相同的公式。
推算过程链接:http://blog.csdn.net/acdreamers/article/details/38929067
这种公式固然优美,时间复杂度为 O(k2) ,但是不可避免地要做除法,如果除数和模数互质那好办,直接逆元即可。但是这里显然不满足。


拉格朗日插值法&牛顿插值法

暑假时HeD大神给我们讲了这两种解法,初中党表示一脸懵逼
拉格朗日插值法其实不难,拿高中选修数论初步看看就懂了。但是还是避免不了除法。
牛顿插值法只需要差分,不需要做除,时间复杂度 O(k2) ,是很优秀的算法,然而我不会。


伯努利数

这个我也不会,但是我有链接:http://blog.csdn.net/acdreamers/article/details/38929067
然而其实它也要做除法。


矩阵乘法

听说可以,然而我并不能YY出来。


第一类斯特林数

BB了这么多,终于到重点了。
第一类斯特林数是什么鬼呢?它有一个圆环排列的定义,但是我并不知道。
于是我便引用它最初发现时的定义。

f(n,m)=i=nm+1ni

f(n,m)=n!(nm)!=n(n1)(n2)...(nm+1)
则第一类斯特林数就表示 n 的各次幂的系数。
其实第一类斯特林数分两种:有符号ss和无符号 su 。有符号就是正常的第一类斯特林数,无符号就是有符号的绝对值。

f(n,m)=i=0mss(m,i)ni

可以发现 ss(n,m)=(1)n+msu(n,m) 。不过我们下面不需要使用 su
至于 ss su 的递推公式,就由大家自己推算了。利用第一类斯特林数的定义很容易推出来,实在不会就参考我的代码。
第一类斯特林数的递推复杂度是 O(k2) 的。
再讲一个显然的东西。
k!Ckj=f(j,k)

这个拆开来约下分就知道了。
还有一个(并不)显然的东西。
Ck+1n+1=i=0nCki

这个使用组合数的杨辉三角递推式拆开就知道了。

下面开始推算

f(j,k)ss(k,k)jk=i=0kss(k,i)jiss(k,k)jk=i=0k1ss(k,i)ji

显然 ss(k,k)=1 ,于是我们移项之后可以得到
jk=f(j,k)i=0k1ss(k,i)ji

j 求和,记Sk(n)表示 1 n k 次幂和得
Sk(n)=j=0n(f(j,k)i=0k1ss(k,i)ji)=j=0n(k!Ckji=0k1ss(k,i)ji)=k!j=0nCkji=0k1ss(k,i)Si(n)=k!Ck+1n+1i=0k1ss(k,i)Si(n)=f(n+1,k+1)k+1i=0k1ss(k,i)Si(n)

这里面的 f 可以O(k)扫一遍,里面一定有且仅有一个 k+1 的倍数,乘的时候处理一下即可。后面部分 O(k) 枚举,所以总的复杂度为 O(k2)
至此问题完美解决。


代码实现

#include <iostream>
#include <cstdio>
#include <cmath>

using namespace std;

typedef long long LL;

const int K=2050;

LL ss[K][K],sum[K];
LL k,n,m,p,ans;

const int A=1000000;

LL mult(LL x,LL y)
{
    LL a1=x/A,a2=x%A,b1=y/A,b2=y%A;
    LL ret=a1*b1%p*A%p*A%p;
    ret+=a1*b2%p*A%p;
    ret+=a2*b1%p*A%p;
    ret+=a2*b2%p;
    return ret%p;
}

LL quick_power(LL x,LL y)
{
    LL ret=1;
    while (y)
    {
        if (y&1)
            ret=mult(ret,x);
        x=mult(x,x);
        y>>=1;
    }
    return ret;
}

void pre()
{
    ss[0][0]=1;
    for (int i=1;i<=k;i++)
        ss[i][0]=0,ss[i][i]=1;
    for (int i=1;i<=k;i++)
        for (int j=1;j<i;j++)
            ss[i][j]=ss[i-1][j-1]+mult(-i+1,ss[i-1][j]);
}

LL calc(LL n)
{
    if (n&1)
        sum[1]=mult(n,(n+1)/2);
    else
        sum[1]=mult(n/2,n+1);
    for (int i=2;i<=k;i++)
    {
        sum[i]=1;
        for (LL j=n+1;j>=n-i+1;j--)
            if (j%(i+1))
                sum[i]=mult(sum[i],j);
            else
                sum[i]=mult(sum[i],j/(i+1));
        for (int j=0;j<i;j++)
            sum[i]=(sum[i]-mult(ss[i][j],sum[j])+p)%p;
    }
    return sum[k];
}

int main()
{
    freopen("box.in","r",stdin);
    freopen("box.out","w",stdout);
    scanf("%lld%lld%lld%lld",&k,&n,&m,&p);
    if (n-m<=5000)
        for (LL i=m;i<=n;i++)
            (ans+=quick_power(i,k))%=p;
    else
    {
        pre();
        ans=(calc(n)-calc(m-1)+p)%p;
    }
    printf("%lld\n",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}
计算一个自然数 $ n $ 在模 $ m $ 意义下的乘法逆元,其核心问题是求解满足 $ nk \equiv 1 \ (\text{mod} \ m) $ 的整 $ k $。这一问题在模运算中具有广泛应用,如加密算法、论分析等领域。 若 $ n $ 与 $ m $ 互质,则存在唯一的乘法逆元。对于求解该逆元的问题,暴力尝试法虽然实现简单,但效率低下,尤其在 $ m $ 很大的情况下,其时间复杂度为 $ O(m) $,不适用于实际应用[^3]。快速算法通常用于求解大指的模运算,如 $ a^b \mod m $,并不直接适用于求解乘法逆元[^4]。线性筛法则主要用于论中的素筛选,不适用于本问题。 扩展欧几里得算法是求解乘法逆元的最优选择,其时间复杂度为 $ O(\log m) $,且能够高效地处理大模的情况。该算法不仅能够判断 $ n $ $ m $ 是否互质,还能在互质的前提下求出满足 $ nk \equiv 1 \ (\text{mod} \ m) $ 的整 $ k $。其基本思想是通过递归地求解 $ ax + by = \gcd(a, b) $ 的整解,最终得到 $ x $ 作为 $ a $ 在模 $ b $ 意义下的逆元(或 $ y $ 作为 $ b $ 在模 $ a $ 意义下的逆元)[^2]。 以下是一个基于扩展欧几里得算法的 Python 实现,用于求解 $ n $ 在模 $ m $ 意义下的乘法逆元: ```python def extended_gcd(a, b): if b == 0: return a, 1, 0 else: g, x1, y1 = extended_gcd(b, a % b) x = y1 y = x1 - (a // b) * y1 return g, x, y def mod_inverse(n, m): g, x, y = extended_gcd(n, m) if g != 1: return None # 逆元不存在 else: return x % m # 确保结果为正 ``` 该实现首先调用 `extended_gcd` 函求解最大公约 $ g $ 及其对应的整解 $ x $ $ y $。若 $ g = 1 $,则 $ x $ 为 $ n $ 在模 $ m $ 意义下的乘法逆元;否则,逆元不存在。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值