hdu5275 (插值法)

来源:互联网 发布:数据录入项目外包 编辑:程序博客网 时间:2024/05/17 03:25

首先要明确,所谓插值,就是多项式的点值表达式转系数表达式。
对于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);        }    }}
0 0
原创粉丝点击