zoukankan      html  css  js  c++  java
  • [整理]多项式多点求值/快速插值

    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)\) 做多点求值。

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

    内容来自_ajthreac_的博客(https://www.cnblogs.com/juruoajh/),未经允许,不得转载。
  • 相关阅读:
    Python、PyCharm的安装及使用方法(Mac版)
    学习Java web技术
    Java Web基础教程
    JavaWeb--深入Servlet与JSP(运行原理)
    Servlet入门总结及第一个Servlet程序
    Mac系统安装和配置tomcat步骤详解
    Struts2进阶学习4
    Struts2进阶学习3
    Struts2基础学习2
    Struts2基础入门
  • 原文地址:https://www.cnblogs.com/juruoajh/p/15673019.html
Copyright © 2011-2022 走看看