拉格朗日插值法

拉格朗日插值法

维基百科链接

  • A ( x ) A(x) A(x) k k k 次多项式,把 k + 1 k+1 k+1 个不同的点值对 ( x i , y i ) (x_i,y_i) (xi,yi) 代入可以唯一地确定这个多项式
  • 一个 k 次多项式,可以由 k + 1 k+1 k+1 k k k 次多项式相加得到。
  • 拉格朗日插值公式为:

L ( x ) = ∑ i = 0 k y i ∏ j = 0 , i ≠ j k x − x j x i − x j L(x)=\sum_{i=0}^{k}y_i\prod_{j=0,i\neq j}^{k} \frac{x-x_j}{x_i-x_j} L(x)=i=0kyij=0,i=jkxixjxxj

  • 其中插值基函数 l i ( x ) l_i(x) li(x) 为:

l i ( x ) = ∏ j = 0 , i ≠ j k x − x j x i − x j l_i(x)=\prod_{j=0,i\neq j}^{k} \frac{x-x_j}{x_i-x_j} li(x)=j=0,i=jkxixjxxj

其中 l i ( x i ) = 1 l_i(x_i)=1 li(xi)=1,其余的 x 0 x_0 x0 x k x_k xk 带入皆为0。这样才能使得 L ( x i ) = y i L(x_i)=y_i L(xi)=yi 成立

通常情况下,我们可以对 x i x_i xi 取连续的值。那么就可以改造一下,取 x 1 = 1 , x 2 = 2 , … , x k + 1 = k + 1 x_1=1,x_2=2,\dots,x_{k+1}=k+1 x1=1,x2=2,,xk+1=k+1,这 k + 1 k+1 k+1 项来进行插值,可以得到

L ( x ) = ∑ i = 1 k + 1 y i ∏ j = 1 , i ≠ j k + 1 x − x j x i − x j = ∑ i = 1 k + 1 y i ∏ j = 1 , i ≠ j k + 1 x − j i − j = ∑ i = 1 k + 1 y i ( x − 1 ) ( x − 2 ) … ( x − ( j − 1 ) ) ( x − ( j + 1 ) ) … ( x − ( k + 1 ) ) ( i − 1 ) ! ( k + 1 − i ) ! ( − 1 ) k + 1 − i L(x)=\sum_{i=1}^{k+1}y_i\prod_{j=1,i\neq j}^{k+1} \frac{x-x_j}{x_i-x_j}\\ =\sum_{i=1}^{k+1}y_i\prod_{j=1,i\neq j}^{k+1} \frac{x-j}{i-j}\\ =\sum_{i=1}^{k+1}y_i \frac{ (x-1)(x-2)\dots(x-(j-1))(x-(j+1))\dots(x-(k+1))}{(i-1)!(k+1-i)!(-1)^{k+1-i}} L(x)=i=1k+1yij=1,i=jk+1xixjxxj=i=1k+1yij=1,i=jk+1ijxj=i=1k+1yi(i1)!(k+1i)!(1)k+1i(x1)(x2)(x(j1))(x(j+1))(x(k+1))

对这个式子计算可以得到模板:

int pref[K+10],suff[K+10],dy[K+10];;
int Lagrange(int k,int dy[],ll x)
{
    if(x<=k+1) return dy[x];
    pref[0]=1;
    for(int j=1; j<=k+1; ++j) pref[j]=1ll*pref[j-1]*(x-j)%mod;
    suff[k+2]=1;
    for(int j=k+1; j>=1; --j) suff[j]=1ll*suff[j+1]*(x-j)%mod;
    ll ans=0;
    for(int i=1; i<=k+1; ++i)
    {
        ll tmp=1ll*dy[i]*pref[i-1]%mod*suff[i+1]%mod*finv[i-1]%mod*finv[k+1-i]%mod;
        if(k+1-i&1) ans=(ans-tmp+mod)%mod;
        else ans=(ans+tmp)%mod;
    }
    return ans;
}

F. The Sum of the k-th Powers

链接:https://codeforces.com/contest/622/problem/F

题意:给定 n、k,计算 ∑ i = 1 n i k   ( 1 ≤ n ≤ 1 0 9 , 0 ≤ k ≤ 1 0 k ) \sum_{i=1}^ni^k\ (1\le n \le 10^9, 0\le k \le 10^k) i=1nik (1n109,0k10k)

思路:带入 x = 1 x = 1 x=1 k + 1 k+1 k+1,这前 k + 1 k+1 k+1 项,进行插值找到第 n n n

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;

ll qpow(ll base,ll n,ll mod)
{
    ll ret=1;
    while(n)
    {
        if(n&1)
            ret=ret*base%mod;
        base=base*base%mod;
        n>>=1;
    }
    return ret;
}

const int K=1e6+10;
int fac[K+10],finv[K+10];

void init()
{
    fac[0]=fac[1]=1;
    for(int i=2; i<=K; ++i)
        fac[i]=1ll*fac[i-1]*i%mod;
    finv[K]=qpow(fac[K],mod-2,mod);
    for(int i=K-1; i>=0; --i)
        finv[i]=1ll*finv[i+1]*(i+1)%mod;
}

int pref[K+10],suff[K+10],dy[K+10];;
int Lagrange(int k,int dy[],ll x)
{
    if(x<=k+1) return dy[x];
    pref[0]=1;
    for(int j=1; j<=k+1; ++j) pref[j]=1ll*pref[j-1]*(x-j)%mod;
    suff[k+2]=1;
    for(int j=k+1; j>=1; --j) suff[j]=1ll*suff[j+1]*(x-j)%mod;
    ll ans=0;
    for(int i=1; i<=k+1; ++i)
    {
        ll tmp=1ll*dy[i]*pref[i-1]%mod*suff[i+1]%mod*finv[i-1]%mod*finv[k+1-i]%mod;
        if(k+1-i&1) ans=(ans-tmp+mod)%mod;
        else ans=(ans+tmp)%mod;
    }
    return ans;
}
int n,k;

int main()
{
    init();
    scanf("%d%d",&n,&k);
    for(int i=1; i<=k+2; ++i)
        dy[i]=qpow(i,k,mod);
    for(int i=1; i<=k+2; ++i)
        dy[i]=(1ll*dy[i]+dy[i-1])%mod;
    printf("%d\n",Lagrange(k+1,dy,n));
    return 0;
}

你可能感兴趣的:(数学,多项式,拉格朗日插值法)