ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[整理]多项式多点求值/快速插值

2021-12-14 17:33:21  阅读:157  来源: 互联网

标签:limits 插值 多项式 mid int 求值 prod


0.多点求值

描述:给定 \(n\) 阶多项式 \(f(x)\),求其 \(m\) 个点值 \(f(a_1),\dots,f(a_m)\)。
乍一看这个东西似乎是不太可做的,我们先考虑如何缩小问题规模也就是多项式阶数。
根据因式定理我们知道一个被 \((x-a)\) 整除的多项式在 \(a\) 处的点值为 \(0\),考虑通过这种方式转化问题。
接下来就比较妙,我们将点值分成左右两半,然后设 \(g_0(x)=\prod\limits_{i=1}^{\lfloor n/2\rfloor}(x-a_i)\),右半边同理设为 \(g_1(x)\)。然后我们发现显然这个东西带入属于沓那一边后都会得到 \(0\),那么一个简单的想法就是直接把 \(f\) 对沓取模,我们以左边为例。
设 \(f(x)=q(x)g_0(x)+r(x)\),沓带入左边的点值会得到 \(r(x)\),而 \(r(x)\) 的次数一定至少减半,所以我们成功缩小了问题规模。
每一步的 \(g_0\) 和 \(g_1\) 可以预先分治处理出来。
由于自带大常数,所以为了卡过板子题采取了小范围暴力的方法,代码可能会很难看:
(略去了多项式板子,可以去另一篇博客里看)

int n,m,F[N],a[N],b[N],*G[N],siz[N];
void Build(int k,int l,int r){//类似线段树建树从下往上合并
  if(r-l<=50){
    siz[k]=r-l+1,G[k]=new int[siz[k]+1],G[k][0]=1;
    for(int i=l;i<=r;i++){
      for(int j=i-l+1;~j;j--)G[k][j+1]=G[k][j];
      G[k][0]=0;
      for(int j=0;j<=i-l+1;j++){
        (G[k][j]+=p-(LL)G[k][j+1]*a[i]%p)%=p;
      }
    }
    return;
  }
  Build(ls,l,nmid),Build(rs,nmid+1,r);
  siz[k]=siz[ls]+siz[rs],G[k]=new int[siz[k]+1];
  Mul(G[ls],G[rs],G[k],siz[ls],siz[rs]);
}
int Q[N];
void Solve(int k,int l,int r,int *f){
  if(r-l<=500){//卡常,经测试大概500左右暴力最快
    for(int i=l;i<=r;i++){
      int res=0;
      for(int j=siz[k]-1;~j;j--){
        res=((LL)res*a[i]%p+f[j])%p;
      }
      b[i]=res;
    }
    return;
  }
  int R[siz[k]+2<<1];
  Divide(f,G[ls],siz[k]-1,siz[ls],Q,R);
  Solve(ls,l,nmid,R);
  Divide(f,G[rs],siz[k]-1,siz[rs],Q,R);
  Solve(rs,nmid+1,r,R);
}
void Print(int x){
  if(x>9)Print(x/10);
  putchar(x%10+48);
}
int main(){
  Read(n),Read(m);
  for(int i=0;i<=n;i++)Read(F[i]);
  for(int i=1;i<=m;i++)Read(a[i]);
  Build(1,1,m);
  if(n>=m)Divide(F,G[1],n,m,Q,F);
  int st=clock();
  Solve(1,1,m,F);
  for(int i=1;i<=m;i++)Print(b[i]),putchar('\n');
  cerr<<(dv)(clock()-st)/CLOCKS_PER_SEC<<endl;
  KafuuChino HotoKokoa
}

转置原理的做法会在之后的某个时间补上

1.快速插值

描述:给定 \(n\) 个点值 \((x_1,y_1),\dots,(x_n,y_n)\),插值出 \(n-1\) 次多项式 \(f(x)\)。
首先搬出来拉格朗日插值公式 \(f(x)=\sum\limits_{i=1}^ny_i\prod\limits_{i\ne j}\dfrac{x-x_j}{x_i-x_j}\),然而沓显然是不能直接用的。
我们先看一下那一坨 \(x_i-x_j\) 的乘积,沓就等于全部的乘积(设为 \(g(x)\))比上去除的乘积 \(\dfrac{\prod_{i=1}^n(x_i-x_j)}{x_i-x_i}\)。咦,变成未定式了,我们洛必达一下发现它变成了 \(g'(x_i)\),于是原式变成了 \(f(x)=\sum\limits_{i=1}^n\dfrac{y_i}{g'(x_i)}\prod\limits_{i\ne j}(x-x_j)\)。
还是不能求,这时候我们应用分治,以 \(f_{l,r}(x)\) 表示区间 \([l,r]\) 内点的插值结果,稍微玩一下式子,某半边的乘积对另一半的贡献是完整的。

\[\begin{aligned} f_{l,r}(x)&=\sum_{i=l}^r\frac{y_i}{g'(x_i)}\left(\prod_{j\in[l,mid],j\ne i}(x-x_j)\right)\left(\prod_{j\in[mid+1,r],j\ne i}(x-x_j)\right)\\ &=f_{l,mid}(x)\prod_{j=mid+1}^r(x-x_j)+f_{mid+1,r}(x)\prod_{j=l}^{mid}(x-x_j) \end{aligned} \]

所以我们就可以分治解决了,复杂度和多点求值相同。该用到的多项式都可以像多点求值里那样分治求出来,然后对 \(g'(x)\) 做多点求值。

代码先不贴了,等卡过常或改进了多点求值再贴,反正理解了原理后代码很好写(

标签:limits,插值,多项式,mid,int,求值,prod
来源: https://www.cnblogs.com/juruoajh/p/15673019.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有