题解 P4781 【【模板】拉格朗日插值】

NaVi_Awson

2018-07-25 22:55:32

Solution

# 拉格朗日插值法 ## 简介 给定 $n + 1$ 个**横坐标不相同**的点,可以唯一确定一个 $n$ 次的多项式。最直观的求多项式的做法就是列方程求解。但是这样需要 $O(n^3)$ 的时间来计算。而拉格朗日插值法则通过构造的方法,得到了一个经过 $n + 1$ 个点的 $n$ 次多项式。 具体的过程是这样的,假设现在我们得到了 $n + 1$ 个点: $$ (x_0, y_0), \;(x_1, y_1),\; \dots,\;(x_n, y_n) $$ 设**拉格朗日基本多项式**为 $$ \ell_j(x) = \prod_{i = 0, i \neq j}^n {x - x_i \over x_j - x_i} $$ 这个基本多项式构造十分巧妙,因为注意到 $\ell_j(x_j) = 1$ ,并且 $\ell_j(x_i) = 0, \;\forall i \neq j$ 。那么,接着构造出这个 $n$ 次多项式 $$ P(x) = \sum_{i = 0}^n y_i\ell_i(x) $$ 根据基本多项式的性质,我们可以知道 $P(x_i) = y_i$ ,也就是经过了这 $n + 1$ 个点。 通过简单的多项式乘法和多项式除法就可以在 $O(n^2)$ 的时间求出这个多项式的系数表达。 ## 求解 已知 $n$ 次多项式 $f(n)$ 上的 $n+1$ 个点 $(x_i,y_i),i\in[0,n]$ ,求 $f(xi)$ ```cpp int lagrange(int n, int *x, int *y, int xi) { int ans = 0; for (int i = 0; i <= n; i++) { int s1 = 1, s2 = 1; for (int j = 0; j <= n; j++) if (i != j) { s1 = 1ll*s1*(xi-x[j])%mod; s2 = 1ll*s2*(x[i]-x[j])%mod; } ans = (1ll*ans+1ll*y[i]*s1%mod*quick_pow(s2, mod-2)%mod)%mod; } return (ans+mod)%mod; } ``` 如果 $x$ 的取值是连续一段的话,我们可以做到 $O(n)$ 求解。假设 $\forall i<j,x_i<x_j$ (具体公式推导的话,如果你有兴趣可以参看之后的内容。因为比较显然,这里不再讲解。) ```cpp int lagrange(int n, int *x, int *y, int xi) { int ans = 0; s1[0] = (xi-x[0])%mod, s2[n+1] = 1; for (int i = 1; i <= n; i++) s1[i] = 1ll*s1[i-1]*(xi-x[i])%mod; for (int i = n; i >= 0; i--) s2[i] = 1ll*s2[i+1]*(xi-x[i])%mod; ifac[0] = ifac[1] = 1; for (int i = 2; i <= n; i++) ifac[i] = -1ll*mod/i*ifac[mod%i]%mod; for (int i = 2; i <= n; i++) ifac[i] = 1ll*ifac[i]*ifac[i-1]%mod; for (int i = 0; i <= n; i++) (ans += 1ll*y[i]*(i == 0 ? 1 : s1[i-1])%mod*s2[i+1]%mod *ifac[i]%mod*(((n-i)&1) ? -1 : 1)*ifac[n-i]%mod) %= mod; return (ans+mod)%mod; } ```