多项式入门——拉格朗日插值
插值用来求解这样一类问题:给定 (n) 点 ((x_i,y_i)) 求过这些点的多项式。
1 简介
设 (f(x)) 为这个多项式,我们有:
[f(x)equiv f(a)mod (x-a) ag{1}
]
这是因为:
[f(x)-f(a)=(a_0-a_0)+a_1(x-a)+a_2(x^2-a^2)+...
]
而后者显然有一个根为 (a) ,所以 ((1)) 式得证。
通过把这 (n) 个点代入我们可以得到:
[egin{cases}
f(x)equiv y_1mod x-x_1\
...\
f(x)equiv y_nmod x-x_n
end{cases}
]
显然模数互质,所以我们考虑用中国剩余定理来解决这个事情。
令 (M=prod_{i=1}^n(x-x_i)) ,(m_i=frac{M}{x-x_i}=prod_{i ot= j}(x-x_j)) 。并且在模 (x-x_i) 的意义下,我们有:
[m_i^{-1}=frac{1}{prodlimits_{i
ot=j}(x_i-x_j)}
]
这是因为我们有:
[prod_{i
ot =j}(x-x_j)equiv prod_{i
ot =j}(x-x_j-x+x_i)=prod_{i
ot =j}(x_i-x_j)
]
所以上述结论成立。
所以我们有:
[f(x)equiv sumlimits_{i=1}^ny_im_im_i^{-1}equiv sumlimits_{i=1}^ny_iprodlimits_{j
ot=i}frac{x-x_j}{x_i-x_j}
]
这个东西可以在 (n^2) 的时间内求出。
2 例题
直接模拟上面的过程即可。
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 2010
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
const ll mod=998244353;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
inline ll ksm(ll a,ll b,ll mod){
ll res=1;
while(b){
if(b&1) (res*=a)%=mod;
a=a*a%mod;
b>>=1;
}
return res;
}
inline ll inv(ll a){
return ksm(a,mod-2,mod);
}
ll n,k,x[N],y[N],ans;
int main(){
read(n);read(k);
for(int i=1;i<=n;i++){
read(x[i]);read(y[i]);
}
for(int i=1;i<=n;i++){
ll fenzi=1,fenmu=1;
for(int j=1;j<=n;j++){
if(j==i) continue;
fenmu*=(x[i]-x[j]);fenmu%=mod;
fenzi*=(k-x[j]);fenzi%=mod;
}
ans+=y[i]*fenzi%mod*inv(fenmu)%mod;ans%=mod;
}
printf("%lld
",(ans%mod+mod)%mod);
return 0;
}
注意:
需要把分母乘出来再求逆元,这样复杂度的瓶颈就不会是求逆元。
结尾注意化为正数。