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

心得

嚷嚷着学这么久 终于在这一天 拿下来拉格朗日插值了

思路来源

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)

若知道(1,x1),(2,x2),...(d,xd)的值,可以O(d)求出每一个多项式上的值

拉格朗日插值(知识整理+公式推导+板子总结)_第1张图片

拉格朗日插值(知识整理+公式推导+板子总结)_第2张图片

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

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

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

板子总结

①已知前n项的值,对于每个询问O(n)处理,以2019ICPC南昌邀请赛B-Polynomial为例

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

拉格朗日插值(知识整理+公式推导+板子总结)_第3张图片

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

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

#include
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,int n)//已知f[0]到f[mx] 求f[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*pre[i-1]*(n-i+1)%mod;
	for(int i=mx;i>=1;--i)
	suf[i-1]=1ll*suf[i]*(n-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=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项的n个(xi,yi),对于每个位置O(n*n)处理,以洛谷P4781为例

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

#include
#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;
}

 

你可能感兴趣的:(知识点总结)