拉格朗日插值

插值真惨

众所周知$k+1$个点可以确定一个$k$次多项式,那么插值就是通过点值还原多项式的过程。

设给出的$k+1$个点分别是$(x_0,y_0),(x_1,y_1),...,(x_k,y_k)$,那么xjb构造一下:

设函数$f_i(x)=\frac{\prod\limits_{j\neq i}(x-x_j)}{\prod\limits_{j\neq i}(x_i-x_j)}\times y_i$

显然这个函数当$x=x_i$时值为$y_i$,$x=x_j(0\leq j\leq k且j\neq i)$时值为0。

求出所有的$f_i$,然后把它们加起来,发现得到的多项式必过给出的这些点,也就是要求的结果了。。。

即要求的多项式$F(x)=\sum\limits_{i=0}^{k}f_i(x)$

易知时间复杂度是$O(k^2)$的,看起来很暴力,但是拉格朗日插值就是这么暴力。。。

如果要动态加点的话可以稍微优化一下:

注意到每个$f_i$中$\prod\limits_{j\neq i}(x-x_j)$重复计算了,可以简化:

设$p(x)=\prod\limits_{i=0}^{k}(x-x_i)$,$q_i=\frac{y_i}{\prod\limits_{j\neq i}(x_i-x_j)}$

则$F(x)=p(x)\times\sum\limits_{i=0}^{k}\frac{q_i}{(x-x_i)}$

这样子原本的复杂度不变,但是新加点时只用重新算一遍$q_i$,复杂度为$O(k)$

这个东西貌似叫重心法?

一个小技巧:

在实际做题的时候,有时不需要根据题目给出的点值插值,而是自己带值插值,此时一般选择$(0,F(0)),...,(k,F(k))$,这时$F$的表达式可以写成:

$F(x)=\sum\limits_{i=0}^{k}\frac{F(i)x(x-1)\cdots(x-k)\times(-1)^{k-i}}{i!(k-i)!}$

就可以预处理阶乘什么的然后$O(k)$做了,但是对膜数有要求

代码(裸插值):

 #include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define inf 2147483647
#define eps 1e-9
#define mod 998244353
using namespace std;
typedef long long ll;
int n,k,x[],y[];
int fastpow(int x,int y){
int ret=;
for(;y;y>>=,x=(ll)x*x%mod){
if(y&)ret=(ll)ret*x%mod;
}
return ret;
}
int lagrange(){
int ret=,t1,t2;
for(int i=;i<n;i++){
t1=,t2=;
for(int j=;j<n;j++){
if(i!=j){
t1=(ll)t1*(k-x[j])%mod;
t2=(ll)t2*(x[i]-x[j])%mod;
}
}
ret=(ret+(ll)t1*y[i]%mod*fastpow(t2,mod-)%mod)%mod;
}
return ret;
}
int main(){
scanf("%d%d",&n,&k);
for(int i=;i<n;i++){
scanf("%d%d",&x[i],&y[i]);
}
printf("%d",(lagrange()+mod)%mod);
return ;
}

题目:

【BZOJ2655】Calc

DP+拉格朗日插值

 #include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define inf 2147483647
#define eps 1e-9
using namespace std;
typedef long long ll;
ll n,m,p,t1,t2,ans=,f[][];
ll fastpow(ll x,ll y){
ll ret=;
for(;y;y>>=,x=(ll)x*x%p){
if(y&)ret=(ll)ret*x%p;
}
return ret;
}
int main(){
scanf("%lld%lld%lld",&m,&n,&p);
f[][]=;
for(ll i=;i<=n*;i++){
for(ll j=;j<=n;j++){
if(!j)f[i][j]=f[i-][j];
else f[i][j]=(f[i-][j]+(ll)f[i-][j-]*i%p*j%p)%p;
}
}
if(m<=n*)return printf("%lld",f[m][n]),;
for(ll i=;i<=n*;i++){
t1=t2=;
for(ll j=;j<=n*;j++){
if(i==j)continue;
t1=(ll)t1*(m-j)%p;
t2=(ll)t2*(i-j)%p;
}
ans=(ans+f[i][n]*t1%p*fastpow(t2,p-)%p)%p;
}
ans=(ans+p)%p;
printf("%lld",ans);
return ;
}

【BZOJ4559】【JLOI2016】成绩比较

组合数DP+拉格朗日插值

 #include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define inf 2147483647
#define eps 1e-9
#define mod 1000000007
using namespace std;
typedef long long ll;
int n,m,k,tmp,nw,u[],r[],g[],C[][],f[][];
int fastpow(int x,int y){
int ret=;
for(;y;y>>=,x=(ll)x*x%mod){
if(y&)ret=(ll)ret*x%mod;
}
return ret;
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=;i<=m;i++){
scanf("%d",&u[i]);
}
for(int i=;i<=m;i++){
scanf("%d",&r[i]);
}
C[][]=;
for(int i=;i<=n+;i++){
C[i][]=;
for(int j=;j<=i;j++){
C[i][j]=(C[i-][j]+C[i-][j-])%mod;
}
}
f[][n-]=;
for(int i=;i<=m;i++){
g[]=u[i];
for(int j=;j<=n;j++){
g[j]=(fastpow(u[i]+,j+)-+mod)%mod;
for(int kk=;kk<j;kk++){
g[j]=(g[j]-(ll)C[j+][kk]*g[kk]%mod+mod)%mod;
}
g[j]=(ll)g[j]*fastpow(j+,mod-)%mod;
}
tmp=;
nw=fastpow(u[i],r[i]-);
int inv=fastpow(u[i],mod-);
for(int j=;j<r[i];j++){
tmp=(ll)(tmp+(ll)((j&)?-:)*C[r[i]-][j]*nw%mod*g[n-r[i]+j]%mod+mod)%mod;
nw=(ll)nw*inv%mod;
}
for(int j=k;j<=n;j++){
for(int kk=j;kk<=n;kk++){
if(n-r[i]-j>=){
f[i][j]=(f[i][j]+(ll)f[i-][kk]*C[kk][j]%mod*C[n-kk-][n-r[i]-j]%mod*tmp%mod)%mod;
}
}
}
}
printf("%d",(f[m][k]+mod)%mod);
return ;
}

【BZOJ3453】XLkxc

差分+拉格朗日插值

 #include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define inf 2147483647
#define eps 1e-9
#define mod 1234567891
using namespace std;
typedef long long ll;
ll t,k,a,n,d,f[],g[],inv[],suf[],pre[];
ll fastpow(ll x,ll y){
ll ret=;
for(;y;y>>=,x=x*x%mod){
if(y&)ret=ret*x%mod;
}
return ret;
}
void _(){
inv[]=inv[]=;
for(int i=;i<=;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for(int i=;i<=;i++)inv[i]=inv[i-]*inv[i]%mod;
}
ll lagrange(ll *s,ll x,ll n){
ll ret=,tmp=;
pre[]=suf[n+]=;
for(int i=;i<=n+;i++)pre[i]=pre[i-]*(x-i+mod)%mod;
for(int i=n+;i;i--)suf[i]=suf[i+]*(x-i+mod)%mod;
for(int i=;i<=n+;i++){
tmp=s[i]*pre[i-]%mod*suf[i+]%mod*inv[i-]%mod*inv[n-i+]%mod;
if((n-i)%==)tmp=-tmp+mod;
ret=(ret+tmp)%mod;
}
return ret;
}
int main(){
_();
scanf("%lld",&t);
while(t--){
scanf("%lld%lld%lld%lld",&k,&a,&n,&d);
for(int i=;i<=k+;i++)g[i]=fastpow(i,k);
for(int i=;i<=k+;i++)g[i]=(g[i-]+g[i])%mod;
for(int i=;i<=k+;i++)g[i]=(g[i-]+g[i])%mod;
f[]=lagrange(g,a,k+);
for(int i=;i<=k+;i++)f[i]=(f[i-]+lagrange(g,(a+i*d)%mod,k+))%mod;
printf("%lld\n",lagrange(f,n,k+));
}
return ;
}

【xsy1537】五颜六色的幻想乡

拆边矩阵树定理+拉格朗日插值

 #include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define inf 2147483647
#define eps 1e-9
#define mod 1000000007
using namespace std;
typedef long long ll;
int N,n,m,s[][],f[],g[],h[],num[],x[],y[],z[];
int fastpow(int x,int y){
int ret=;
for(;y;y>>=,x=(ll)x*x%mod){
if(y&)ret=(ll)ret*x%mod;
}
return ret;
}
int calc(int xx){
int ret=,pw=fastpow(xx,n),tmp,inv;
memset(s,,sizeof(s));
for(int i=;i<=m;i++){
tmp=(z[i]==)?xx:(z[i]==)?pw:;
s[x[i]][y[i]]=(s[x[i]][y[i]]-tmp)%mod;
s[y[i]][x[i]]=(s[y[i]][x[i]]-tmp)%mod;
s[x[i]][x[i]]=(s[x[i]][x[i]]+tmp)%mod;
s[y[i]][y[i]]=(s[y[i]][y[i]]+tmp)%mod;
}
for(int i=;i<n;i++){
tmp=;
for(int j=i;j<n;j++){
if(s[j][i]){
tmp=j;
break;
}
}
if(!tmp)return ;
if(tmp!=i){
ret=-ret;
for(int j=i;j<n;j++)swap(s[i][j],s[tmp][j]);
}
ret=(ll)ret*s[i][i]%mod;
inv=fastpow(s[i][i],mod-);
for(int j=i+;j<n;j++){
if(s[j][i]){
int t=(ll)s[j][i]*inv%mod;
for(int k=i;k<n;k++){
s[j][k]=(s[j][k]-(ll)s[i][k]*t%mod+mod)%mod;
}
}
}
}
return ret;
}
void gao(){
f[]=;
for(int i=;i<=N;i++){
for(int j=i;j>=;j--)f[j+]=f[j];
f[]=;
for(int j=;j<=i;j++)f[j]=(f[j]-(ll)f[j+]*i%mod+mod)%mod;
}
for(int i=;i<=N;i++){
int tmp=;
h[N+]=;
for(int j=N;j>=;j--){
if(i!=j)tmp=(ll)tmp*(i-j)%mod;
h[j]=(f[j+]+(ll)h[j+]*i)%mod;
}
tmp=(ll)fastpow(tmp,mod-)*num[i]%mod;
for(int j=;j<=N;j++){
g[j]=(g[j]+(ll)tmp*h[j])%mod;
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=m;i++){
scanf("%d%d%d",&x[i],&y[i],&z[i]);
}
N=n*n-n;
for(int i=;i<=N;i++){
num[i]=calc(i);
}
gao();
for(int i=;i<=n;i++){
for(int j=;j<n-i;j++){
printf("%d\n",(g[i+j*n]+mod)%mod);
}
}
return ;
}

【BZOJ4162】shlw loves matrix II

其实这是道常系数齐次线性递推模板题哒

 #include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define inf 2147483647
#define eps 1e-9
#define mod 1000000007
using namespace std;
typedef long long ll;
typedef double db;
struct lambda{
int id,x;
lambda(){}
lambda(int _id,int _x){
id=_id,x=_x;
}
}p[];
int n,a[][],sq[][],tmp[][],dt[][],x[],pw[],t[],f[],g[],ans[][];
char s[];
int fastpow(int x,int y){
int ret=;
for(;y;y>>=,x=(ll)x*x%mod){
if(y&)ret=(ll)ret*x%mod;
}
return ret;
}
int det(){
int ret=,nw,tmp;
for(int i=;i<=n;i++){
nw=i;
for(int j=i;j<=n;j++){
if(dt[j][i]){
nw=j;
break;
}
}
if(nw!=i){
for(int j=i;j<=n;j++){
swap(dt[i][j],dt[nw][j]);
}
ret=-ret;
}
for(int j=i+;j<=n;j++){
if(dt[j][i]){
tmp=(ll)dt[j][i]*fastpow(dt[i][i],mod-)%mod;
for(int k=i;k<=n;k++){
dt[j][k]=(dt[j][k]-(ll)dt[i][k]*tmp%mod)%mod;
}
}
}
ret=(ll)ret*dt[i][i]%mod;
}
return (ret+mod)%mod;
}
void mul(int *s,int *x,int *y){
for(int i=;i<=n*;i++)t[i]=;
for(int i=;i<n;i++){
for(int j=;j<n;j++){
//add(t[i+j],(ll)x[i]*y[j]%mod);
t[i+j]=(t[i+j]+(ll)x[i]*y[j]%mod)%mod;
}
}
int mo=fastpow(f[n],mod-);
for(int i=n*-;i>=n;i--){
for(int j=n-;j>=;j--){
//add(t[i+j-n],mod-(ll)f[j]*t[i]%mod*mo%mod);
t[i+j-n]=(t[i+j-n]-(ll)f[j]*t[i]%mod*mo%mod)%mod;
}
}
for(int i=;i<n;i++){
s[i]=t[i];
}
}
int main(){
scanf("%s%d",s,&n);
for(int i=;i<=n;i++){
for(int j=;j<=n;j++){
scanf("%d",&a[i][j]);
}
}
for(int i=;i<=n;i++){
memcpy(dt,a,sizeof(a));
for(int j=;j<=n;j++)dt[j][j]-=i;
p[i]=lambda(i,det());
}
for(int i=;i<=n;i++){
for(int j=;j<=n;j++)g[j]=;
g[]=p[i].x;
for(int j=;j<=n;j++){
if(i!=j){
g[]=(ll)g[]*fastpow(p[j].id-p[i].id,mod-)%mod;
}
}
for(int j=;j<=n;j++){
if(i!=j){
for(int k=n;k;k--){
g[k]=((ll)g[k]*p[j].id%mod-g[k-])%mod;
}
g[]=(ll)g[]*p[j].id%mod;
}
}
for(int j=;j<=n;j++){
f[j]=(f[j]+g[j])%mod;
}
}
x[]=pw[]=;
for(int i=strlen(s)-;i>=;i--){
if(s[i]=='')mul(pw,pw,x);
mul(x,x,x);
}
for(int i=;i<=n;i++){
sq[i][i]=;
}
for(int i=;i<n;i++){
for(int j=;j<=n;j++){
for(int k=;k<=n;k++){
//add(ans[j][k],(ll)sq[j][k]*pw[i]%mod);
ans[j][k]=(ans[j][k]+(ll)sq[j][k]*pw[i]%mod)%mod;
}
}
memset(tmp,,sizeof(tmp));
for(int j=;j<=n;j++){
for(int k=;k<=n;k++){
for(int l=;l<=n;l++){
//add(tmp[j][k],(ll)sq[j][l]*a[l][k]%mod);
tmp[j][k]=(tmp[j][k]+(ll)sq[j][l]*a[l][k]%mod)%mod;
}
}
}
memcpy(sq,tmp,sizeof(tmp));
}
for(int i=;i<=n;i++){
for(int j=;j<=n;j++){
printf("%d ",(ans[i][j]%mod+mod)%mod);
}
puts("");
}
return ;
}

剩下道【NOI2012】骑行川藏,神仙题,咕咕咕

快速插值

本部分参考了yww的博客:Orzyww

先讲个东西:多点求值

多点求值

给出一个$k$次多项式$A(x)$和$n$个点$x_0,x_1,...,x_{n-1}$,求多项式在这$n$个点的值;

显然暴力是$O(nk)$的,因此要优化。

考虑一个简(shen)单(xian)的构造:构造多项式$B_i(x)=x-x_i$,$C_i(x)=A(x) \mod B_i(x)$,易知$B_i(x_i)=0$,所以$A(x_i)=C_i(x_i)$;

但是这样计算$B_i$和$C_i$依然是$O(n)$的,还需要优化;

可以考虑类似多项式求逆取模那样分治倍增。

先把当前点集$X=\{x_0,x_1,...,x_{n-1}\}$分成两半:

$X_0=\{x_0,x_1,...,x_{\lfloor\frac{n}{2}\rfloor -1}\}$

$X_1=\{x_{\lfloor\frac{n}{2}\rfloor},x_{\lfloor\frac{n}{2}\rfloor+1},...,x_{n-1}\}$

然后构造多项式$B_0,B_1$类似把$B$分成两半,$A$同理:

$B_0(x)=\prod\limits_{i=0}^{\lfloor\frac{n}{2}\rfloor-1}(x-x_i)$

$B_1(x)=\prod\limits_{i=\lfloor\frac{n}{2}\rfloor}^{n-1}(x-x_i)$

$A_0(x)=A(x) \mod B_0(x)$

$A_1(x)=A(x) \mod B_1(x)$

容易看出,这样分治下去当$x∈X_0$时,$A(x)=A_0(x)$,否则$A(x)=A_1(x)$,可以每次递归处理。

每层时间复杂度是$O(nlogn)$的,根据主定理,总的时间复杂度就是:

$T(n)=2T(\frac{n}{2})+O(nlogn)=O(nlog^2n)$

快速插值

拉格朗日插值是$O(n^2)$的,只有特殊情况才能优化到$O(n)$,而快速插值法可以在任意情况下做到$O(nlog^2n)$的时间复杂度的插值。

快速插值法是基于拉格朗日插值法进行数学优化的,那么先来回顾一下拉格朗日插值公式:

$F(x)=\sum\limits_{i=0}^{n}\frac{\prod\limits_{j\neq i}(x-x_j)}{\prod\limits_{j\neq i}(x_i-x_j)}\times y_i$

主要问题就是怎么快速求分子分母。

先考虑分母,设$g_i=\prod\limits_{j\neq i}(x_i-x_j)$,则:

$g_i=\lim\limits_{x \to x_i}\frac{\prod\limits_{j=0}^{n}(x-x_j)}{x-x_i}$

$=(\prod\limits_{j=0}^{n}(x-x_j))'|_{x=x_i}$

于是就可以分治求出$\prod\limits_{j=0}^{n}(x-x_j)$,求导后对所有$x_i$多点求值即可;

分子也可以类似分治求,处理好分母然后顺手合并答案就好了。

时间复杂度易证是$O(nlog^2n)$

代码(多点求值+快速插值):

太难了,先咕着

拉格朗日插值&&快速插值的更多相关文章

  1. 洛谷P5158 【模板】多项式快速插值

    题面 传送门 前置芝士 拉格朗日插值,多项式多点求值 题解 首先根据拉格朗日插值公式我们可以暴力\(O(n^2)\)插出这个多项式,然而这显然是\(gg\)的 那么看看怎么优化,先来看一看拉格朗日插值 ...

  2. Note/Solution -「洛谷 P5158」「模板」多项式快速插值

    \(\mathcal{Description}\)   Link.   给定 \(n\) 个点 \((x_i,y_i)\),求一个不超过 \(n-1\) 次的多项式 \(f(x)\),使得 \(f(x ...

  3. 【洛谷P5158】 【模板】多项式快速插值

    卡常严重,可有采用如下优化方案: 1.预处理单位根 2.少取几次模 3.复制数组时用 memcpy 4.进行多项式乘法项数少的时候直接暴力乘 5.进行多项式多点求值时如果项数小于500的话直接秦九昭展 ...

  4. P3270 [JLOI2016]成绩比较(拉格朗日插值)

    传送门 挺神仙的啊-- 设\(f[i][j]\)为考虑前\(i\)门课程,有\(j\)个人被\(B\)爷碾压的方案数,那么转移为\[f[i][j]=\sum_{k=j}^{n-1}f[i-1][k]\ ...

  5. 插值代码17个---MATLAB

    函数名 功能Language 求已知数据点的拉格朗日插值多项式Atken 求已知数据点的艾特肯插值多项式Newton 求已知数据点的均差形式的牛顿插值多项式Newtonforward 求已知数据点的前 ...

  6. vue简介,插值表达式,过滤器

    目录 VUE框架介绍 what?什么是vue? why?为什么要学习vue? special特点? how如何使用? 下载安装? 导入方式? 挂在点el 插值表达式 delimiters自定义插值表达 ...

  7. matlab数据插值

    由图可见采样点前段比较稀疏,比较有规律,后段比较密集,比较复杂 这里的spline是三次样条插值 随着次数的增高,曲线在两端震荡的越来越剧烈 用上其他插值的方法 线性插值 最近点插值 分段三次米勒插值 ...

  8. Jacobi与SOR迭代法的实现与性能比较及均匀间距与Chebyshev插值的实现、性能分析及二者生成的插值误差比较

    这篇文章给出(1)Jacobi与SOR迭代法的实现与性能比较及(2)均匀间距与Chebyshev插值的实现.性能分析及二者生成的插值误差比较,给出完整的实现代码,没有进行性能优化,仅供参考. (1)J ...

  9. mapboxgl 中插值表达式的应用场景

    目录 一.前言 二.语法 三.对地图颜色进行拉伸渲染 1. 热力图 2. 轨迹图 2. 模型网格渲染 四.随着地图缩放对图形属性进行插值 五.interpolate的高阶用法 六.总结 一.前言 in ...

随机推荐

  1. Codeforces Round #284 (Div. 2) A

    解题思路:给出 n个电影的精彩时段(a[i],b[i]),和每次可以跳过的时间x,问要看完所有的精彩时刻,至少需要看多长时间的电影. 因为要时间最少,所有除了精彩时刻的电影则能跳过就跳过(用取余来算) ...

  2. map————两个数组的交集(2)

    class Solution { public: vector<int> intersect(vector<int>& nums1, vector<int> ...

  3. 服务器上安装anaconda

    1.在anaconda网站下载安装包: 清华镜像网站:https://repo.continuum.io/archive/index.html 2.下载最新版本为python3 ,Linux64位的: ...

  4. UVALive-8078 Bracket Sequence 简单dp

    题目链接:https://cn.vjudge.net/problem/UVALive-8078 题意 括号序列T是这样定义的: T是个空的 T是(T), {T}, 或者 [T] T是两个T组成的,比如 ...

  5. mac上用VMWare虚拟机装Linux-Ubuntu

    1.下载vmware fusion for Mac 网上有很多,大家自己找吧 2.安装Vmware 3.序列号可以在网上找,或者淘宝几块钱买一个(记得买对应版本的) 4.之后就下一步,完成就好 安装U ...

  6. [宏]_IO, _IOR, _IOW, _IOWR 宏的用法与解析

    在驱动程序里, ioctl() 函数上传送的变量 cmd 是应用程序用于区别设备驱动程序请求处理内容的值.cmd除了可区别数字外,还包含有助于处理的几种相应信息. cmd的大小为 32位,共分 4 个 ...

  7. VUE:条件渲染和列表渲染

    条件渲染 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <titl ...

  8. js滚动事件实现滚动触底加载

    移动端触底加载时前端开发过程中常用功能,主要是通过三个值的大小来进行判断: 首先介绍jquery的写法,代码如下: $(window).scroll(function(){ var windowH=$ ...

  9. jquery-jquery异步提交表单插件

    使用jquery.form可以异步提交文件或者表单,下面的代码演示了如何提交文件 http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js <s ...

  10. android开发面试题

    找了将近两个星期的工作,面试了5家公司,罗列一下笔试或者面试时的问题,祝大家好运 1,handler机制 答:handler执行机制:1).在主线程中创建handler 2).子线程中借助主线程的ha ...