拉格朗日插值法
程序员文章站
2022-05-18 07:56:07
...
拉格朗日插值法是一种多项式插值方法。简单来说,给定 个点,它可以唯一地构造出经过这些点的 次函数。
如何实现呢?其实非常地暴力:记我们给定的点为 ,我们只需要构造出一些函数 使得 满足 ,, 即可。
接下来介绍“开关函数” 。 满足当 时 ,, 时 。这样 。
如何实现上述功能呢?我们考虑一个简单的数学性质:,,。这样我们只需要让 时分子分母一定相等, 为其他的 值时分母一定出现 即可。可以得到:。考虑这样的 为什么拥有上述性质:当 时,无论 为何值,分子分母一定相等,即 ;当 , 时,由于 ,故必然存在 ,此时分子为 ,。
综上所述,我们可以给出一般地过点集 的多项式插值公式:
注意其中 , 和 的区别。
例题
The sum of the K-th Powers
给出两个非负整数 ,,求
答案对 取模。
,。
题解
暴力计算显然实现复杂度无法承受。
记 。
首先分析当 较小的情况。 时,; 时,;依此类推,可以发现 是关于 的 次多项式。
既然如此,我们可以通过先暴力算出一部分多项式上的点,然后通过插值得出多项式关于 的函数形式,再直接通过函数求值。
我们知道,确定一个 次函数需要 个点,故我们首先暴力算出 。然后,我们将这些点代入拉格朗日插值公式得
然后就到了愉快的拆式子时间了。
这样只需要通过预处理得出分母中阶乘的逆元,并通过费马小定理求出 的逆元即可在 的时间内得出答案。
代码
#include <iostream>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const unsigned MOD = 1E9 + 7;
const unsigned MAXN = 1E6;
const unsigned N = MAXN + 10;
unsigned n, k;
unsigned inv[N];
unsigned f[N];
void init() {
inv[0] = inv[1] = 1;
for (int i = 2; i <= k + 2; i++) {
inv[i] = (-ll(MOD / i) * inv[MOD % i] % MOD + MOD) % MOD;
}
for (int i = 2; i <= k + 2; i++) {
inv[i] = ull(inv[i - 1]) * inv[i] % MOD;
}
}
unsigned pow(unsigned a, unsigned b) {
unsigned res = 1;
while (b) {
if (b & 1) {
res = ull(res) * a % MOD;
}
a = ull(a) * a % MOD;
b >>= 1;
}
return res;
}
main() {
ios::sync_with_stdio(false);
cin >> n >> k;
init();
for (unsigned i = 1; i <= k + 2; i++) {
f[i] = (f[i - 1] + pow(i, k)) % MOD;
}
if (n <= k + 2) {
cout << f[n];
return 0;
}
unsigned t = 1;
for (unsigned i = 1; i <= k + 2; i++) {
t = ull(t) * (n - i) % MOD;
}
unsigned ans = 0;
for (unsigned i = 1; i <= k + 2; i++) {
ans = (ans + ((ll(ull(f[i]) * t % MOD * pow(n - i, MOD - 2) % MOD * inv[i - 1] % MOD * inv[k + 2 - i] % MOD) * (((k + 2 - i) & 1) ? -1 : 1)) + MOD) % MOD) % MOD;
}
cout << ans;
}
上一篇: python天天向上的力量 B
下一篇: Java 版插值算法