hdu5275 (插值法)

本文详细介绍了两种常用的插值方法——拉格朗日插值法和牛顿插值法,并通过实例代码展示了这两种方法的具体应用过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先要明确,所谓插值,就是多项式的点值表达式转系数表达式。
对于n个x不相同的点(xi,yi),可以唯一确定一个阶为n的多项式

y=c0+c1x+c2x2+c3x3+...+cn1xn1

一般的,有两种常用的插值方法:
1.拉格朗日插值法
直接构造多项式
y=k=0n1ykjk(xjxk)jk(xxj)

简单粗暴,证明只需要把 x0,x1,..xn1 带入,看对应y值是否与给定的相同就可以了
2.牛顿插值
虽然拉格朗日插值法比较简单也便于记忆,但对于一些要对子串进行插值的题目,牛顿插值显然更加方便
y=c0+c1(xx0)+c2(xx1)(xx0)+...cn1(xx0)...(xxn2)

注意对于 x0,x1..xn1 进行插值,上式并不包含 xn1 ,而计算 c0,c1...cn1 的方法就是分别把 (x0,y0),(x1,y1)...(xn1,yn1) 代入,看上去很麻烦,但是引入差商表之后, ci 的计算显得异常容易。
定义
xi0yi,xikf(i,k)=f(i+1,k1)f(i,k1)xi+kxi

根据定义进行计算可以发现 c0,c1...ci 其实就是 f(0,0),f(0,1)...f(0,i)
更进一步的,假如你想对 (xl,yl),(xl+1,yl+1)...(xr,yr)
对应插值所得的系数就是 f(l,0),f(l,1)...f(l,rl)

对于上面插值的理解,可以参考我hdu5275的代码
首先是很适合给子串插值的牛顿插值

#include<bits/stdc++.h>
using namespace std;
typedef long long Int;
const int M=1e9+7;
int rev[250002];
int dp[3002][3002];
int x[3002],y[3002];
inline int getrev(int x){return x<0?-rev[-x]:rev[x];}
int main()
{
    rev[1]=1;
    for(int i=2;i<=250000;i++)rev[i]=(M-M/i)*(Int)rev[M%i]%M;
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++)scanf("%d%d",x+i,y+i);
        for(int i=1;i<=n;i++)dp[0][i]=y[i];
        for(int i=1;i<=n;i++)
            for(int j=1;j+i<=n;j++)
                dp[i][j]=((dp[i-1][j+1]-dp[i-1][j])*(Int)getrev(x[i+j]-x[j])%M+M)%M;
        int m;scanf("%d",&m);
        while(m--)
        {
            int l,r,q;scanf("%d%d%d",&l,&r,&q);
            int ans=0,cur=1;
            for(int i=0;i<=r-l;i++)
            {
                ans+=dp[i][l]*(Int)cur%M;
                if(ans>=M)ans-=M;
                cur=(cur*(Int)(q-x[l+i])%M+M)%M;
            }printf("%d\n",ans);
        }
    }
}

然后是拉格朗日插值法,对于本题需要一些观察和技巧

#include<bits/stdc++.h>
using namespace std;
const int Maxn=3002,M=1e9+7;
typedef long long Int;
int x[Maxn],y[Maxn];
int n,m;
int dp[Maxn][Maxn];
int rev[250002],frev[250002];
int powmod(int x,int y)
{
    int t=x,ret=1;
    while(y)
    {
        if(y&1)ret=ret*(Int)t%M;
        y>>=1;
        t=t*(Int)t%M;
    }
    return ret;
}
inline int getrev(int x)
{
    if(x<0)return frev[-x];
    return rev[x];
}
int main()
{
    for(int i=1;i<=250000;i++)rev[i]=powmod(i,M-2),frev[i]=powmod(M-i,M-2);
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++)scanf("%d%d",x+i,y+i);
        scanf("%d",&m);
        for(int i=1;i<=n;i++)
        {
            dp[i][i]=1;
            for(int j=i-1;j>=1;j--)dp[i][j]=dp[i][j+1]*(Int)getrev(x[i]-x[j])%M;
            for(int j=i+1;j<=n;j++)dp[i][j]=dp[i][j-1]*(Int)getrev(x[i]-x[j])%M;
        }
        while(m--)
        {
            int l,r,q;
            scanf("%d%d%d",&l,&r,&q);
            int cs=-1,ans=0;
            int tot=1;
            for(int i=l;i<=r;i++)
            {
                if(x[i]!=q)tot=((q-x[i])*(Int)tot%M+M)%M;
                else{cs=i;}
            }
            if(cs!=-1)
            {
                ans=y[cs]*(Int)dp[cs][l]%M*dp[cs][r]%M*tot%M;
            }
            else
            {
                for(int k=l;k<=r;k++)
                {
                    ans+=y[k]*(Int)dp[k][l]%M*dp[k][r]%M*tot%M*getrev(q-x[k])%M;
                    if(ans>=M)ans-=M;
                }
            }printf("%d\n",ans);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值