学文化课学累了所以学个拉格朗日插值法来摸鱼
要求
给定 \(n\) 个不同的点,确定一条可以表示经过这 \(n\) 个点的线的 \(n - 1\) 次表达式。
分析
初中生都能想到,可以用待定系数法列出方程组,并转化为矩阵乘法的形式来求解。但是这种思路比较低效,使用高斯消元求出多项式的系数的时间复杂度是 \(O(n^3)\)。
而拉格朗日插值法却可以在 \(O(n^2)\) 的时间复杂度求解系数。
公式
设拉格朗日基函数为
\[\ell_j(x) = \prod _{i = 0, i \ne j}^{n}\frac{x - x_i}{x_j - x_I}
\]
所求多项式为
\[P(x) = \sum_{i = 0}^{n} y_i \ell_i(x)
\]
解析
看不懂吗?别急。
先看一看基函数,看起来有点吓人不是吗?但事实上稍加观察后发现并不复杂。
基函数具有以下性质,或者说本来就是为了以下性质而构造:\(\ell_i(x_i) = 1\),且 \(\ell_i(x_j) = 0\),(\(i \ne j\))。即第 \(i\) 个拉格朗日基函数只在 \(x_i\) 处对 \(P(x)\) 贡献为 \(1\),在别点(\(x_j,\forall j \ne i\))对 \(P(x)\) 贡献为 \(0\),从而可以精准匹配所有给定数据点。
然后 \(P(x)\) 的工作原理就显而易见了。
于是我们在 \(O(n^2)\) 的时间复杂度求解出来了系数。
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int mod = 998244353;
const int maxn = 2e3 + 5;
ll x[maxn], y[maxn];
ll binpow(ll x, ll y) {
if (x == 1 || y == 0)
return 1;
ll res = 1;
while (y) {
if (y & 1)
res = res * x % mod;
x = x * x % mod;
y = y >> 1;
}
return res;
}
int main() {
int n, k; cin >> n >> k;
for (int i = 1; i <= n; i ++)
cin >> x[i] >> y[i];
ll ans = 0;
for (int i = 1; i <= n; i ++) {
ll up = 1, down = 1;
for (int j = 1; j <= n; j ++) {
if (i == j)
continue;
up = up * ((k - x[j] + mod) % mod) % mod;
down = down * ((x[i] - x[j] + mod) % mod) % mod;
}
ll inv = binpow(down, mod - 2);
ll point = y[i] * up % mod;
point = point * inv % mod;
ans = ans + point % mod;
}
cout << ans % mod << '\n';
return 0;
}
拓展
当取值为连续时,我们可以在 \(O(n)\) 的时间复杂度求解问题,更加高效。
但是我懒了,来日再写。

浙公网安备 33010602011771号