引入 在初中时,我们都学过一次函数,一定也都做过这种题:
给定两个点,求经过这两个点的一次函数的解析式。
我们只需要待定系数法,然后把点坐标带进去解方程即可。
那么推广一下,可以得到结论:
在平面直角坐标系中, 个点能唯一确定一个 次多项式。
但如果我们暴力高斯消元,复杂度是 的。
而拉格朗日插值法可以在 的复杂度内解决这个问题。
单点求值 设 为待求多项式, 为第 个点的坐标。
以下均保证 互不相同。
设拉格朗日基本多项式 为
这个多项式十分巧妙。
可以发现,。
根据基本多项式,我们可以构造出 :
根据基本多项式的性质,我们可以得到 ,也就是恰好经过这 个点。
那么单点求值时,只需要把那个点带进去即可。
复杂度
事实上应该线性求逆元才是严格 的,但我懒了,所以写的是 的。
例题:【模板】拉格朗日插值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include "bits/stdc++.h" #define re register #define int long long using namespace std;const int maxn=2e3 +10 ,mod=998244353 ;int n,k,ans;int x[maxn],y[maxn];inline int qpow (int a,int b) { int res=1 ; while (b){ if (b&1 ) res=res*a%mod; b>>=1 ; a=a*a%mod; } return res; }inline int Inv (int x) {return qpow (x,mod-2 );}signed main () {#ifndef ONLINE_JUDGE freopen ("1.in" ,"r" ,stdin); freopen ("1.out" ,"w" ,stdout);#endif ios::sync_with_stdio (0 ),cin.tie (0 ),cout.tie (0 ); cin>>n>>k; for (re int i=1 ;i<=n;++i) cin>>x[i]>>y[i]; for (re int i=1 ;i<=n;++i){ int s1=y[i],s2=1 ; for (re int j=1 ;j<=n;++j){ if (i!=j){ s1=s1*((k-x[j])%mod+mod)%mod; s2=s2*((x[i]-x[j])%mod+mod)%mod; } } ans=(ans+s1*Inv (s2)%mod)%mod; } cout<<ans; return 0 ; }
求出系数 现在我们已经可以做到 复杂度的单点求值了。
但这还不够,因为我想具体求出来这个多项式。
把上面那个东西拿过来:
提出常数部分:
然后设 ,则
我们可以暴力的把 这个多项式算出来,这是 的。
然后对于每一项,我们暴力模拟长除法,总复杂度是 的。
综上,我们在 的复杂度内得到了多项式的系数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #include "bits/stdc++.h" #define re register #define int long long using namespace std;const int maxn=2e3 +10 ,mod=998244353 ;int n,k;int x[maxn],y[maxn];int f[maxn],g[maxn];int a[maxn];int tmp[maxn];inline int qpow (int a,int b) { int res=1 ; while (b){ if (b&1 ) res=res*a%mod; b>>=1 ; a=a*a%mod; } return res; }inline int Inv (int x) {return qpow (x,mod-2 );}inline void pre () { for (re int i=1 ;i<=n;++i){ int res=1 ; for (re int j=1 ;j<=n;++j){ if (i!=j) res=res*(x[i]-x[j]+mod)%mod; } a[i]=y[i]*Inv (res)%mod; } g[0 ]=1 ; for (re int i=1 ;i<=n;++i,swap (g,tmp)){ tmp[0 ]=0 ; for (re int j=1 ;j<=i;++j) tmp[j]=g[j-1 ]; for (re int j=0 ;j<=i;++j) tmp[j]=(tmp[j]+g[j]*(-x[i]+mod)%mod)%mod; } }inline void Lagrange () { for (re int i=1 ;i<=n;++i){ int lst=0 ,inv=Inv (-x[i]+mod); for (re int j=0 ;j<n;++j){ tmp[j]=(g[j]-lst+mod)*inv%mod; f[j]=(f[j]+a[i]*tmp[j]%mod)%mod,lst=tmp[j]; } } }inline int calc (int x) { int ans=0 ,res=1 ; for (re int i=0 ;i<n;++i){ ans=(ans+res*f[i]%mod)%mod; res=res*x%mod; } return ans; }signed main () {#ifndef ONLINE_JUDGE freopen ("1.in" ,"r" ,stdin); freopen ("1.out" ,"w" ,stdout);#endif ios::sync_with_stdio (0 ),cin.tie (0 ),cout.tie (0 ); cin>>n>>k; for (re int i=1 ;i<=n;++i) cin>>x[i]>>y[i]; pre (); Lagrange (); cout<<calc (k); return 0 ; }
x 取值连续时的优化 此时式子变为
对于分子,我们维护前缀积和后缀积。
对于分母,可以发现这是一个阶乘形式。
所以式子变为
当 为奇数时,分母应为负号。
复杂度
例题:求 。
The Sum of the k-th Powers
可以证明, 为 次多项式。
所以直接取 个点把它插出来即可。
取值连续,所以可以做到 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include "bits/stdc++.h" #define re register #define int long long using namespace std;const int maxn=1e6 +10 ,mod=1e9 +7 ;int n,m,k,ans;int fac[maxn],pre[maxn],suf[maxn];inline int qpow (int a,int b) { int res=1 ; while (b){ if (b&1 ) res=res*a%mod; b>>=1 ; a=a*a%mod; } return res; }inline int Inv (int x) {return qpow (x,mod-2 );}signed main () {#ifndef ONLINE_JUDGE freopen ("1.in" ,"r" ,stdin); freopen ("1.out" ,"w" ,stdout);#endif ios::sync_with_stdio (0 ),cin.tie (0 ),cout.tie (0 ); cin>>n>>k;m=k+2 ; pre[0 ]=fac[0 ]=suf[m+1 ]=1 ; for (re int i=1 ;i<=m;++i) pre[i]=pre[i-1 ]*(n-i)%mod; for (re int i=m;i>=1 ;--i) suf[i]=suf[i+1 ]*(n-i)%mod; for (re int i=1 ;i<=m;++i) fac[i]=fac[i-1 ]*i%mod; int y=0 ; for (re int i=1 ;i<=m;++i){ y=(y+qpow (i,k)%mod)%mod; int s1=pre[i-1 ]*suf[i+1 ]%mod; int s2=((m-i)&1 ?-1ll :1ll )*fac[i-1 ]*fac[m-i]%mod; ans=(ans+(y*s1%mod*Inv (s2)%mod)%mod+mod)%mod; } cout<<ans; return 0 ; }