各类求自然数幂和方法

高斯消元

我们知道:
\[\sum_{i=1}^{n}i=\frac{n(n+1)}{2}\]
以及:
\[\sum_{i=1}^{n}i^2=\frac{n(n+1)(2n+1)}{6}\]
以及:
\[\sum_{i=1}^{n}i^3=(\sum_{i=1}^{n}i)^2=(\frac{n(n+1)}{2})^2\]
那我们可以猜想,自然数的\(k\)次幂和对应的公式是一个次数为\(k+1\)的没有常数项的多项式(实际上也是的)。
证明吗,暂时不会。。。

However,我们可以拿这个猜想做题。

设这个\(k+1\)次的多项式\(f(x)=\sum_{i=1}^{k+1}a_ix^i\)
利用待定系数法,我们只需要知道\(k+2\)对\((x,f(x))\),列出方程组就能解出所有的\(a_i\),从而就能代入更大的\(x\)求出\(f(x)\)。

由于解方程组需要用到高斯消元算法,时间复杂度是\(O(k^3)\),在\(k\leq 100\)的范围内还是能无压力解决的。

总结

时间复杂度:\(O(k^3)\)
空间复杂度:\(O(k^2)\)

由于高斯消元时要在模意义下做除法,对于模数不是质数的情况无法适应,而且时间复杂度难以接受,不是一种较常用的方法。

第二类斯特林数

分析

定义\(S(n,m)\)表示\(n\)个有差别的球放入m个无差别的盒子中的方案数,要求盒子不能为空。

容易得到下面的递推式:
\[S(n,m)=S(n-1,m-1)+mS(n-1,m)\]
考虑新加入的球,要么放在新的盒子里,要么放在之前的盒子里。因为球是有差别的,所以放在任意一个盒子里的方案都是不一样的,因此\(S(n-1,m)\)要乘上一个\(m\)。

要用它解决自然数幂和问题,还是要用到第二类斯特林数的一个性质:
\[a^k=\sum_{i=0}^{k}S(k,i)i!C_a^i\]
这个性质还是很好解释的,我们可以把\(a^k\)当做\(k\)个有差别的球,放入\(a\)个有差别的盒子的方案数,盒子可以为空。
那么我们就枚举\(i\)个盒子被放满了,\(S(k,i)\)只保证了球有差别,乘以\(i!\)相当于给盒子编号,令盒子也有差别,最后乘上一个\(C_a^{i}\)表示在\(a\)个盒子中选\(i\)个的方案数。

那么就可以开始化自然数幂求和的式子:
\(\sum_{a=1}^{n}a^k\)
\(=\sum_{a=1}^{n}\sum_{i=0}^{k}S(k,i)i!C_a^i\)
两个sigma没有关联,我们可以交换枚举顺序:
\(=\sum_{i=0}^{k}S(k,i)i!\sum_{a=1}^{n}C_a^i\)
由于\(a<i\)时\(C_a^{i}=0\),又可以化成:
\(=\sum_{i=0}^{k}S(k,i)i!\sum_{a=i}^{n}C_a^i\)

继续化简需要用到一个性质:
\(\sum_{a=i}^{n}C_a^i=C_{n+1}^{i+1}\)
证明考虑运用组合数递推公式即:
\(C_i^j=C_{i-1}^j+C_{i-1}^{j-1}\)
\(C_{n+1}^{i+1}\)
\(=C_n^i+C_n^{i+1}\)
\(=C_n^i+C_{n-1}^i+C_{n-1}^{i+1}\)
\(=C_n^i+C_{n-1}^i+C_{n-2}^i+C_{n-2}^{i+1}\)
继续化下去就会得到:
\(=\sum_{a=i}^{n}C_a^i\)

性质就得证了,上面的式子就化简为:
\(=\sum_{i=0}^{k}S(k,i)i!C_{n+1}^{i+1}\)
组合数有点麻烦,我们展开为阶乘形式:
\(=\sum_{i=0}^{k}S(k,i)i!\frac{(n+1)!}{(i+1)!(n-i)!}\)
拆开\((i+1)!=i!*(i+1)\):
\(=\sum_{i=0}^{k}S(k,i)\frac{(n+1)!}{(i+1)(n-i)!}\)
发现\(\frac{(n+1)!}{(n-i)!}\)其实是\(i+1\)个连续整数相乘,其中必有一个是\(i+1\)的倍数,因此式子一定取整数,就不用考虑模数的问题了。

那么直接枚举这\(i+1\)个连续的整数,得到了时间复杂度为\(O(k^2)\)的算法。计算斯特林数和求自然数幂的复杂度都是\(O(k^2)\),总复杂度就是\(O(k^2)\)。

Code

附带分解乘法黑科技

#include <cstdio>
#include <cstring>

typedef long long ll;
const int N = 2007;

ll k, n, p, s[N][N];

ll multi(ll a, ll b)
{
    ll x1 = a / 1000000, x2 = a % 1000000, y1 = b / 1000000, y2 = b % 1000000;
    return (x1 * 1000000 % p * y1 % p * 1000000 % p + x1 * 1000000 % p * y2 % p + y1 * 1000000 % p * x2 % p + x2 * y2 % p) % p;
}

ll solve(ll n)
{
    if (n == 0) return 0;
    ll ret = 0;
    for (int i = 1; i <= k && i <= n; i++)
    {
        ll sum = s[k][i];
        for (ll j = n - i + 1; j <= n + 1; j++)
            if (j % (i + 1) == 0) sum = multi(sum, j / (i + 1));
            else sum = multi(sum, j);
        ret = (ret + sum) % p;
    }
    return ret;
}

int main()
{
    scanf("%lld%lld%lld%lld", &k, &n, &p);
    s[0][0] = 1;
    for (int i = 1; i <= k; i++)
        for (int j = 1; j <= i; j++)
            s[i][j] = (s[i - 1][j - 1] + multi(j, s[i - 1][j])) % p;
    printf("%lld\n", solve(n) % p);
    return 0;
}

总结

时间复杂度:\(O(k^2)\)
空间复杂度:\(O(k^2)\)

这种做法由于不用除法而适用于模数为任意数的情况,但是求斯特林数复杂度是\(O(k^2)\)的,当\(k\)较大时不再适用。

拉格朗日插值法

大坑待填。。。。

原文地址:https://www.cnblogs.com/zjlcnblogs/p/10327830.html

时间: 2024-11-08 21:26:13

各类求自然数幂和方法的相关文章

求自然数幂和

求自然数幂和,就是一条公式,然后用代码实现: 公式描述如下: 可以看出只要我们预处理出每一项,就可以在线性时间内求得自然数的幂和.前面的倒数可以用递推法求逆元 预处理,组合数也可以预处理,也可以先预处理,现在关键是如何预处理伯努利数. 伯努利数满足条件,且有 那么继续得到 这就是伯努利数的递推式,逆元部分同样可以预处理. 代码: typedef long long ll; typedef unsigned long long ull; const ll N=2005; const ll mod=

自然数幂和取模问题进一步探究

在上次的文章 http://blog.csdn.net/acdreamers/article/details/38929067 中,学习了求自然数幂 和的有效方法,并且了解了求伯努利数的优美算法.今天,我们来看两个简单的问题. 问题:求的值,满足,和. 分析:很容易看出连续个数的结果是相同的,即循环节长度为,对于,需要先进行降幂,所用公式如下 最坏时间复杂度约为. 问题:求的值.其中和,并且是奇数. 分析:首先,我们来关注.由 到了这里,由于是奇数,那么. (1)如果为奇数,则刚好全部抵消,答案

伯努利数与自然数幂和

今天我们讨论的问题是如何有效地求自然数的幂和.接下来以3个经典题目为例来讲解. 题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1864 分析:其实求自然数的幂和方法有很多种,先来看看普通的递推求法,由于 那么对于所有的累加得到 进一步得到 可以看出这是一个递推式,如果我们记 那么得到如下递归式 递归出口是 为了提高效率,在递归的时候需要记忆化.由于要用到大数,用Java实现. 代码: import java.math.

zoj 1864 自然数幂和

zoj 1864 题目链接: 点击打开链接 题意: 求自然数幂和. 限制: 0 <= n <= 10^50; 1 <= k <= 100 思路: k不大,而且答案不取模,直接搞 (n+1)^(k+1) - n^(k+1) = C(k+1,1)*n^k + C(k+1,2)*n^(k-1) + ... + C(k+1,k)*n + 1; (n+1)^(k+1) - 1 = ( (n+1)^(k+1) - n^(k+1) ) + ( n^(k+1) - (n-1)^(k+1) ) +

康复计划#3 简单常用的几种计算自然数幂和的方法

本篇口胡写给我自己这样的东西都忘光的残废选手 以及暂时还不会自然数幂和的人- 这里大概给出最简单的几种方法:扰动法(化为递推式),斯特林数(离散微积分),高阶差分(牛顿级数),伯努利数(指数生成函数)- 不同方法的思维难度.普适程度.实现难度.时间复杂度上面都有差异-同时自然数幂和是探究各种求和方法的经典例子,了解多一点它的做法对于处理各种求和问题是有所帮助的- 问题:求$\sum_{k=0}^{n} k^t$,其中$t \in \mathbb{N}$是一个常数.要求求解的时间复杂度与$n$无关

求解自然数幂和的若干种方法

问题的引入 给定\(n,k\)求\[\sum_{i=1}^ni^k\] 1. 循环 四年级应该会循环了. 能做到\(O(nk)\)的优秀时间复杂度. 2. 快速幂 五年级学了快速幂之后就能做到\(O(nlog_2k)\) 请不要小看这个算法.有时候在特定的情况下(例如\(n\)很小,或\(1\rightarrow n\)的距离变得很小时),这个复杂度真的很优秀. 3. 差分法 六年级应该知道差分和二项式定理了.那么:\[(a+1)^k-a^k=\sum_{i=0}^{k-1}C_k^ia^i\]

UVA766 Sum of powers(1到n的自然数幂和 伯努利数)

自然数幂和: (1) 伯努利数的递推式: B0 = 1 (要满足(1)式,求出Bn后将B1改为1 /2) 参考:https://en.wikipedia.org/wiki/Bernoulli_number http://blog.csdn.net/acdreamers/article/details/38929067 使用分数类,代入求解 #include<cstdio> #include<iostream> #include<cstdlib> #include<

自然数幂和

首先我来介绍一下什么是自然数幂和: 1+2+3+...+i+...+n=? 12+22+32+...+i2+...+n2=? 1k+2k+3k+...+ik+...+nk=? 类似上述式子的就是自然数幂和了,那么具体怎么求呢,这就是今天的重点了: 1+2+3+...+i+...+n=? 这个式子大家肯定都会求,这就是一个等差数列求和,然后套公式: S=(n?n+1)2 那么 1k+2k+3k+...+ik+...+nk这样的式子怎么求呢. 首先求 (n+1)k+1?nk+1???????????

Codeforces 622F The Sum of the k-th Powers ( 自然数幂和、拉格朗日插值法 )

题目链接 题意 : 就是让你求个自然数幂和.最高次可达 1e6 .求和上限是 1e9 分析 :  题目给出了最高次 k = 1.2.3 时候的自然数幂和求和公式 可以发现求和公式的最高次都是 k+1 那么大胆猜测幂为 k 的自然数幂和肯定可以由一个最高次为 k+1 的多项式表示 不会证明,事实也的确如此 此时就变成了一个拉格朗日插值的模板题了 只要先算出 k+2 个值.然后就能确定最高次为 k+1 的多项式 套模板求值即可 当然自然数幂和不止这一种做法.例如伯努利数.斯特林数等 详细可参考 ==