拉格朗日插值法
概述
-
拉格朗日插值法(下简称拉插)是一种多项式单点求值的算法。
-
对于任意的 \(K\) 次多项式,我们可以利用其已知的 \(K+1\) 个或更多的点唯一确定该多项式的形式,且拥有比高消更为优秀的复杂度。
实现原理
-
在已知 \(K\) 次多项式 \(f(x)\) 的 \(K+1\) 个点后,我们可以利用 Gauss 消元来 \(O(K^3)\) 地求出其各项系数,查询复杂度则是 \(O(K)\)。注意到明显的不平衡,设法设计一种方式能 \(O(K^2)\) 之类地同时支持构建和查询。
-
不妨记已知点为 \((k,v)\),考虑构建这么一个多项式:\(f(x)=\sum\limits_{i=1}^{K+1} v_i\times g_i(x)\),使得 \(g_i(x)=\begin{cases} 1 & x=k_i\\ 0 & otherwise\end{cases}\)。
-
可以证明该函数符合已知的 \(K+1\) 个点,于是其唯一确定,从而该函数即为原函数。
-
考虑 \(g\),可以这么配 \(g\):\(g_i(x)=\prod\limits_{j\ne i}^{} \dfrac{x-k_j}{k_i-k_j}\)。目的在于使得 \(x=k_i\) 时,分子分母全部互相消去, \(x\ne k_i\) 时,有一项分子为 \(0\) 的效果。
-
综合得 \(f(x)=\sum\limits_{i=1}^{K+1} v_i \times \prod\limits_{j\ne i}^{} \dfrac{x-k_j}{k_i-k_j}\) ,可以在 \(n^2\) 的时间内求值,并且可以在模意义下进行。
-
一个小 tip:把一个 \(K\) 次多项式认为成 \(K+?(?\geqslant0)\) 次多项式是合法的,因为这相当于高次项系数赋成 \(0\)。在不是很确定到底是多少次的时候,强烈建议 \(+7/+10/...\),以防由于前缀和/差分/...带来的升次而挂掉。
连续性拉插优化
-
在我们自行构造的多项式中,可以考虑将 \(k\) 取连续的 \(1\sim n\)。此时会有非常美妙的性质:
-
原式可以化为 \(f(x)=\sum\limits_{i=1}^{k+1} v_i\times \dfrac{\prod\limits_{j\ne i}^{} x-j}{\prod\limits_{j\ne i}^{} i-j}\) 。
-
对于分子,可以 \(O(n)\) 求前缀积与后缀积;
-
对于分母,显而易见它是一个类阶乘 \((i-1)!\times (-1)^{K+1-i}\times (K+1-i)!\) 。进一步地,其逆元是可以预处理的。
-
所以能把 \(O(n^2)\) 优化到 \(O(n)\),比较常见的用法是在确保是多项式的 dp 中加速求解,如 \(\text{P5469 [NOI2019] 机器人}\)。
-
下面给出优化版本的封装函数式代码,其中 \(K\) 为多项式次数,\(k\) 为所求点。
il ll lagrange(ll K,ll k){
static ll prof[maxk],prob[maxk],numer,denom,ret;//prod
prof[0]=prob[K+2]=1,ret=0;
for(ll i=1;i<=K+1;++i) prof[i]=prof[i-1]*(k-i)%mod;
for(ll i=K+1;i;--i) prob[i]=prob[i+1]*(k-i)%mod;
For(i,1,K+1){
numer=prof[i-1]*prob[i+1]%mod; if(numer<0) numer+=mod;
denom=finv[i-1]*finv[K+1-i]%mod;
if((K+1-i)&1 && denom) denom=mod-denom;//需要考虑 denom=0 的情况
ret=(ret+v[i]*numer%mod*denom)%mod;
}
return ret;
}
例题
P5469 [NOI2019] 机器人
23.1.12 T1 实用和
- 版权原因,请移步查看。同校的可以找我要截图。