拉格朗日插值(知识整理+公式推导+板子总结)

心得

2019年7月2日,整理总结

2020年4月16日,更新O(n)的情形,注意值连续时,拉格朗日插值可以优化到O(n)(n是多项式次数)

思路来源

https://www.cnblogs.com/zwfymqz/p/10063039.html(自为风月马前卒大佬)

https://www.cnblogs.com/cjyyb/p/9392388.html(O(n*n)板子)

https://www.cnblogs.com/cjyyb/p/9392911.html(洛谷P4781题解)

https://blog.csdn.net/BeNoble_/article/details/79512449(化简证明1)

https://blog.csdn.net/weixin_38686780/article/details/81155608(化简证明2)

https://blog.csdn.net/GodJing007/article/details/91348487(南昌邀请赛拉格朗日插值)

https://blog.csdn.net/GodJing007/article/details/90957805(重心拉格朗日插值)

知识整理(截图自化简证明1+化简证明2)

①如果n次多项式已知的点是连续的,求一个值的复杂度是O(n)的,

即对于n+1次多项式f(x),若知道(x_1,y_{1}),...,(x_{n},y_{n})的值,对于一个给定的x,可以O(n)地求出f(x)的值

公式:P(x)=\sum_{i=0}^n(-1)^{n-i}P(i)\frac{x(x-1)(x-2)...(x-n)}{(n-i)!i!(x-i)}

证明:

分母是阶乘的逆元,可在O(d)时间复杂度下预处理,

分子前半段是前缀积,后半段是后缀积,可在n给定的情形下O(d)处理

对于每个n,O(d)的求和即可,这里d=k+1

 

例题

2019ICPC南昌邀请赛B-Polynomial

已知前n项的值,对于每个询问O(n)处理

把所有ll改成int会快很多,所以mod数小于int32时,数组都开int

先插出f(n+1)的值,然后根据n+2个值,预处理前缀和函数sum(0)到sum(n+1)

对于询问l和r,插出sum(r)和sum(l-1)单点作差即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=9999991;
const int N=1e3+10;
int t,n,m,l,r;
int a[N],sum[N],pre[N],suf[N];
int fac[N],finv[N];
int modpow(int x,int n,int p)
{
	int res=1;
	for(;n;x=1ll*x*x%p,n>>=1)
	if(n&1)res=1ll*res*x%p;
	return res;
} 
int cal(int *f,int mx,ll n)//已知f[0]到f[mx] 求f[n] 注意n的范围
{   
	if(n<=mx)return f[n];
        int ans=0;
	pre[0]=suf[mx]=1;
	for(int i=1;i<=mx;++i)
	pre[i]=1ll*(n-i+1)%mod*pre[i-1]%mod;//注意到(n-i+1)*pre[i-1]在有些题可能爆ll 先%
	for(int i=mx;i>=1;--i)
	suf[i-1]=1ll*(n-i)%mod*suf[i]%mod;
	for(int i=0;i<=mx;++i)
	{
		int sg=(mx-i)&1?-1:1;
		ans=ans+1ll*sg*pre[i]%mod*suf[i]%mod*finv[i]%mod*finv[mx-i]%mod*f[i]%mod;
		if(ans>=mod)ans-=mod;
		if(ans<0)ans+=mod;
	}
        return ans;
}
void init()
{
	fac[0]=1;
	for(int i=1;i<N;++i)
	fac[i]=1ll*fac[i-1]*i%mod;
	finv[N-1]=modpow(fac[N-1],mod-2,mod);
	for(int i=N-1;i>=1;--i)
	finv[i-1]=1ll*finv[i]*i%mod;
}
int main()
{
    init();
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<=n;i++)
        scanf("%d",&a[i]);
        a[n+1]=cal(a,n,n+1);//插出f(n+1)
        sum[0]=a[0];//非常关键 别忘了 
        for(int i=1;i<=n+1;i++)
        sum[i]=(sum[i-1]+a[i])%mod;
        while(m--)
	{
            scanf("%d%d",&l,&r);
            int cnt=cal(sum,n+1,r)-cal(sum,n+1,l-1);
            if(cnt<0)cnt+=mod;
            printf("%d\n",cnt);
        }
    }
    return 0;
}

②如果n次多项式已知的点不是连续的(是任意的),求一个值的复杂度是O(n^2)的,

公式:P(x)=\sum_{i=0}^{n}P(x_i)\prod_{j=0,j\ne i}^{n}\frac{x-x_j}{x_i-x_j}

证明:

举个例子理解一下,假设给出的三个点为(1,3),(2,7),(3,13),直接把P(x)展开,

P(x) = 3 \frac{(x - 2)(x - 3)}{(1 - 2)(1 - 3)} + 7\frac{(x-1)(x-3)}{(2 - 1)(2-3)} + 13\frac{(x-1)(x-2)}{(3 -1)(3-2)}

观察不难得到,如果我们把x_{i}带入的话,除第i项外的每一项的分子中都会有x_{i}-x_{i}为0,

这样其他的所有项就都被消去了,必有P(x_{i})=y_{i}成立

例题

洛谷P4781

给出非前n项的n个(xi,yi),对于每个位置O(n^2)处理

代码来自:https://www.cnblogs.com/cjyyb/p/9392911.html

#include<cstdio>
#define ll long long
#define MOD 998244353
#define MAX 2020
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n,K,x[MAX],y[MAX],ans;
int fpow(int a,int b)
{
    int s=1;
    while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}
    return s;
}
int main()
{
    n=read()-1;K=read();
    for(int i=0;i<=n;++i)x[i]=read(),y[i]=read();
    for(int i=0;i<=n;++i)
    {
        int tmp=1;
        for(int j=0;j<=n;++j)
            if(i!=j)tmp=1ll*tmp*(K-x[j])%MOD*fpow(x[i]-x[j],MOD-2)%MOD;
        ans=(ans+1ll*y[i]*tmp)%MOD;
    }
    ans=(ans+MOD)%MOD;printf("%d\n",ans);
    return 0;
}

 

  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值