明明有最厉害最好想的插值做法,怎么没有人写呢。
思路
考虑 \(n\) 个点可以确定一个 \(n-1\) 次多项式。
如何确定。
令 \(l_i(x)=\prod_{j\not =i}\frac{(x-x_j)}{(x_i-x_j)}\)。
可以发现这个多项式在 \(x=x_i\) 时值为一,在 \(x=x_j(j\not = i)\) 时值为零。
那么就有:
\[F(x)=\sum_{i=0}^{i<n}y_il_i(x)
\]
容易发现这个多项式恰好满足上面的条件,当然,这就是拉格朗日插值。
如何得到这个多项式?
可以先求出:
\[G(x)=\prod(x-x_i)
\]
发现:
\[l_i(x)=\frac{G(x)}{(x-x_i)k_i}
\]
其它的是一个常数所以和起来写成 \(k_i\) 即可。
那么就可以 \(O(n^2)\) 求解了。
思路
#include <bits/stdc++.h>
using namespace std;#define int long longint mod;inline int power(int x, int y) {int res = 1;while (y) {if (y & 1) res = res * x % mod;x = x * x % mod, y /= 2;}return res;
}inline vector<int> lagrange(const vector<int> &x, const vector<int> &y) {int n = x.size();vector<int> a(n + 1, 0), f(n, 0);a[0] = 1;auto add = [&](int x) {for (int j = n; j >= 1; j--)a[j] = (a[j - 1] - a[j] * x) % mod;a[0] = -a[0] * x % mod;};for (int i = 0; i < n; i++) add(x[i]);for (int i = 0; i < n; i++) {if (x[i] == 0) {for (int j = 0; j <= n; j++) a[j] = a[j + 1];a[n] = 0;} else {int iv = power(x[i], mod - 2);a[0] = -a[0] * iv % mod;for (int j = 1; j <= n; j++) {a[j] = a[j] - a[j - 1];a[j] = -a[j] * iv % mod;}}int s = 1;for (int j = 0; j < n; j++)if (i != j) s = s * (x[i] - x[j]) % mod;s = power(s, mod - 2) * y[i] % mod;for (int j = 0; j < n; j++)f[j] = (f[j] + a[j] * s) % mod;add(x[i]);}for (int i = 0; i < n; i++) f[i] = (f[i] % mod + mod) % mod;return f;
}signed main() {ios::sync_with_stdio(0), cin.tie(0);vector<int> a, b, f;cin >> mod;for (int i = 1, x; i <= mod; i++) {cin >> x;a.push_back(i - 1);b.push_back(x);}f = lagrange(a, b);for (int i = 0; i < mod; i++)cout << f[i] << " \n"[i == mod - 1];return 0;
}