忘光了,所以复习【POL】

防爬标记:作者 TOBapNw-cjn,网址 https://www.cnblogs.com/TOBapNwCJN/p/16530792.html。

更新于 2022/8/30 17:30

拉格朗日插值

给定 n 个点 Pi(Xi,Yi) ,求过这 n 个点的唯一一个多项式。

朴素拉格朗日插值

只求 f(k)

Qi(Xi,0),是 Pix 轴上的投影。

所以,如果构造了 n 个多项式 gi(1in),使得 giPiQj(ji),那么 f=i=1ngi 就是答案。

接下来构造:gi=YijixXjXiXj,使得对于 jigi(Xj)=0j=i 恰好 gi(Xi)=Yi

所以:

f=i=1nYijixXjXiXj

这样就可以 O(n2) 快速求 f(k) 了。

例:Luogu P4781 【模板】拉格朗日插值

带入即可。


坐标连续时的拉格朗日插值

Xi=i,只求 f(k)

代入得 f=i=1nYijixjij

仔细观察一下,可以发现,我们只要预处理前后缀 jxj 即可。

所以:

f=i=1nYiprei1sufi+1(i1)!(ni)!(1)(ni)

就可以 O(n) 了。

例:CodeForces 622F

设答案为一个 k+1 次多项式 f(x)=i=0k+1aixi

直接带入前 k+2 个点,然后用坐标连续时的拉格朗日插值即可。


重心拉格朗日插值

支持动态加点。

考虑:

f=d=1n(xXd)i=1nYi(xXi)ji(XiXj)

然后维护 a=d=1n(xXd)bi=Yiji(XiXj)

可以做到 O(n) 修改,O(n) 查询。


求出多项式每一项的系数

提出系数,那么 f=i=1nYi(ji1XiXj)j=1n(xXj)xXi

所以预处理时先暴力卷出 j=1n(xXj),再每个地方暴力除以 xXi,最后乘上系数。

复杂度 O(n2)

然后我就写了一个程序得出了以下著名函数:

f(x)=11450924x457254512x3+400781524x2286271312x+114509

f(x)=2862730x5286272x4+4866596x34294052x2+392191415x114508

多项式乘法

快速傅里叶变换(FFT)

快速卷积。

离散傅里叶变换(DFT)

DFT,把一个多项式从系数表示法转化为点值表示法。

为什么要转成点值表示法呢?

考虑两个多项式乘起来,只要装换成点值,然后把点值相乘即可。

单位根

设两个多项式次数分别为 n,m

大多数想法是取任意 n+m+1 个点(因为答案是 n+m 次),但是可能 TLE。

所以考虑一些特殊点。

这时候就要取到单位根了。n 次单位根记作 ωn,即为 xn=1 的所有复数解。

一共有 n 个单位根,所以记作 ωn0,ωn1,,ωnn1,并且以幅角大小排序。

在复数域中只有以 O 为圆心半径为 1 的圆上的点才有可能是单位根。

性质

对于上标 n<0 的情况,可认为上标在模 n 意义下,即 ωnk=ωnkmodn

  • ωn0=1

    定有一个根 1

  • ωnk=(ωn1)k

    考虑乘上一个 ωn1 表示把当前向量转一个 1n 的圆周。

  • (ωnj)k=ωnjk

    由上一条简单得出。

  • ωnjωnk=ωnj+k

    考虑 (ωn1)j(ωn1)k=(ωn1)j+k=ωnj+k

  • ω2n2k=ωnk

    仔细想一想就知道是对的,并且 ωpnpk=ωnk

  • 对于一个偶数 n,有 ωn(k+n2)=ωnk

    ωn(k+n2)=ωnkωnn2

    ωnn2 看做旋转 12 圈即可。

求单位根

首先 ωn0(1,0),然后三角函数随便算就可以知道 ωn1(cos2πn,sin2πn)

考虑 ωn1 表示旋转 1n 圆周。那么 ωni=ωni1ωn1

快速傅里叶变换(FFT)

考虑一个多项式 f(x)=i=0n1aixi,把它按次数奇偶性分为两部分。

此部分设 m=n2

即:

f(x)=i=0m1a2ix2i+i=0m1a2i+1x2i+1

这里需要保证 n=2p(pn),这样保证不会出现除尽的情况,如果 n 不能表示为 2p,那就高位补 0,以下设 。

然后设:

f0(x)=i=0m1aixi,f1(x)=i=0m1aixi

则:

f(x)=f0(x2)+xf1(x2)

代入单位根:

  • 代入 ωnk(k<m)

    f(ωnk)=f0((ωnk)2)+ωnkf1((ωnk)2)=f0(ωmk)+ωnkf1(ωmk)

  • 代入 ωnk+m(k<m)

f(ωnk+m)=f0((ωnk+m)2)+ωnk+mf1((ωnk+m)2)=f0(ωn2k+n)+ωnk+mf1(ωn2k+n)=f0(ωn2k)+ωnk+mf1(ωn2k)=f0(ωmk)+ωnk+mf1(ωmk)=f0(ωmk)ωnkf1(ωmk)

我们把二者对比一下:之差了一个符号!这时候就开始体会到单位根的牛了。

在这个时候,如果我们知道了 ωmk(k<m)f0,f1 下的点值,我们就可以 O(n) 推出 ωnk(k<n)f 下的点值。

那么我们就可以递归求出所有点值,不难发现递归共 logn 层。

所以复杂度为 O(nlogn)

快速傅里叶逆变换(IFFT)

首先有一个结论:

g=DFT(f)nf(k)=i=0n1(ωnk)ig(i)

证明:

首先有:

g(k)=i=0n1(ωnk)if(i)

i=0n1(ωnk)ig(i)=i=0n1j=0n1ωnikωnijf(j)=i=0n1j=0n1ωni(jk)f(j)

讨论:

  • j=k

    ωn0=1,所以 f(k) 一共有 n 次被计算到,答案为 nf(k)

  • jk

    p=jk,所以贡献为 i=0n1ωnipf(k+p)=ωnpf(k+p)i=0n1ωni

    可以发现后面有个等比数列求和:i=0n1ωni=ωnnωn0ωn11=0

所以就证完了。

可以发现根据这个式子,我们只要改变一下单位根的旋转方向(把旋转一个单元变为 ωn1),并且在最后除以一个 n,就变成了 FFT。

具体的,只要改变单位根里的一个符号即可。

代码

代码(处理单位根):

inline void init(Ci N,const bool op){
    o.resize(max(N,2));
    o[0]={1,0};o[1]={cos(PI/N),(op?1:-1)*sin(PI/N)};
    for(int i=2;i<N;i++)o[i]=o[i-1]*o[1];
}

代码(FFT):

void fft(vector<Cp>&f,Ci n,const bool op){
    if(n==1)return;
    vector<Cp>f0,f1;int m=n>>1;
    f0.resize(m);f1.resize(m);
    for(int i=0;i<m;i++)f0[i]=f[i<<1],f1[i]=f[i<<1|1];
    fft(f0,m,op);fft(f1,m,op);
    init(m,op);
    for(int i=0;i<m;i++){
        f[i]=f0[i]+o[t][i]*f1[i];
        f[i+m]=f0[i]-o[t][i]*f1[i];
    }
}

代码(多项式乘法):

inline void fft(){fft(x,x.size(),1);}
inline void Ifft(){fft(x,x.size(),0);}
inline friend Poly operator*(Poly U,Poly V){
    int S=U.size()+V.size()-1;
    int u=1;while(u<S)u<<=1;
    U.resize(u);V.resize(u);
    U.fft();V.fft();
    for(int i=0;i<u;i++)U[i]*=V[i];
    U.Ifft();
    for(int i=0;i<u;i++)U[i].x/=u;
    return U;
}

蝴蝶变换

我们的递归版是递归到最下面然后合并上来。

我们直接到最下面然后直接合并。

考虑按奇偶性分组的本质是按当前二进制位向左或向右,正好与原标号二进制相反,所以最后一层的标号即为位置序号的翻转。

感觉不太说的清楚,看图:

v9Vzcj.png

比如上图中第 4 个位置标号为 (011)(2),填的数是 (110)(2),即 6

接下来,如何快速翻转二进制呢?

设翻转后的数组为 r,那么 ri=ri22+[imod2=1]2S1

Sr 的长度,2S1 即为最高位。

把二进制串 a1a2an 除以 20a1an1,翻转后 anan1a1,变为 an1an2a10

可以发现只要删去最后一位,再加个最高位就行了。

代码(迭代 FFT):

for(int l=2,t=1;l<=n;l<<=1,t++)
    for(vector<Cp>::iterator i=f.begin();i!=f.end();i+=l){
        int m=l>>1;
        for(int j=0;j<m;j++){
            Cp tmp=o[(n>>t)*j]*i[j+m];
            i[j+m]=i[j]-tmp;i[j]+=tmp;
        }
    }

例:[ZJOI2014]力

给出 n 个数 q1,q2,qn,定义

Fj=i=1j1qi×qj(ij)2  i=j+1nqi×qj(ij)2

Ei = Fiqi

1in,求 Ei 的值。1n1050<qi<109

这个东西其实是一个普通卷积和一个减法卷积,分别算即可。

快速数论变换(NTT)

NTT

由于 FFT 需要用到单位根,不可避免的就会有精度误差,而且也有较大的常数,数学家们已经证明,在复数域中,单位根是唯一一类满足要求的数。

不过 OI 中不少题在模意义下,所以我们考虑同余系中的原根。

对于模数 p,有一个原根 g

可以构造在模意义下满足单位根性质的 ωnk=g(p1)kn

不难发现需要满足 n|(p1),而著名模数 998244353=223×7×17+1

这在 n=2k 时基本可以用。

任意模数NTT

用 3 个 NTT 模数,最后 CRT 合并即可。

模数表

分治 FFT/NTT

考虑 n 个二项多项式卷起来或者一个多项式自己对自己的递推式,可以用分治做到 O(nlog2n)

关于递推的分治方法就是每次算左半边对右半边的贡献。

多项式求逆

由于 kn 时,f(x) 在模 xk 意义下的逆元等于其在模 xn 意义下的,所以我们可以把模 xn 补为模 xk

s=2u,t=2u1

f 在模 xt 意义下的逆元为 G,在模 xs 意义下的为 g,考虑从 G 推到 g

首先有:

fg1(modxs)(1)fG1(modxs)(2)(1)(2)=Gg0(modxs)G22Gg+g20(modxt)G2f2G+g0(modxt)g2GG2f(modxt)

所以就可以递推了。

复杂度 T(n)=T(n2)+O(nlogn)=O(nlogn)

多项式带余除法

q(x),r(x) 满足 f(x)=q(x)g(x)+r(x)

f 的次数为 ng 的次数为 m

x 变成 1x

f(x1)=q(x1)g(x1)+r(x1)xnf(x1)=xnmq(x1)xmg(x1)+xnr(x1)

fr(x)=xdff(x1)df 表示多项式 f 的次数,其实不难发现 fr(x) 就是 f(x) 的系数 reverse 一下。

fr(x)=qr(x)gr(x)+xnm+1rr(x)fr(x)=qr(x)gr(x)(modxnm+1)qr(x)=fr(x)gr(x)(modxnm+1)r(x)=f(x)q(x)g(x)

可以发现用一下求逆即可。

多项式对数函数(ln)

g(x) 满足 g(x)lnf(x)(modxn)

g(x)f(x)f(x)(modxn)g(x)f(x)f(x)(modxn)

导数和积分都可以直接来。

多项式指数函数(exp)

g(x) 满足 g(x)ef(x)(modxn)

牛顿迭代

不难发现上面的方法做不了。

引入牛顿迭代的问题:

g(x) 满足 f(g(x))0(modxn)

考虑求逆的递推思路,设 s=2u,t=2u1

假设当前求出了 f(G(x))0(modxt)

f(g(x))G(x) 处泰勒展开:

i=01i!f(i)(G(x))(g(x)G(x))if(g(x))0(modxs)

可以发现,gGmodxt 的意义下是一样的。

所以 g(x)G(x) 只剩下 xt 及以上次项,所以 i2 时,(g(x)G(x))i 的最低非 0 位在 s 以上,modxs0

f(G(x))+f(G(x))(g(x)G(x))0(modxs)g(x)G(x)f(G(x))f(G(x))(modxs)

复杂度同求逆:O(nlogn)

exp

g(x)ef(x)(modxn)lng(x)f(x)(modxn)lng(x)f(x)0(modxn)

F(g(x))=lng(x)f(x),然后直接带入牛迭的式子:

g(x)=G(x)(lnG(x)f(x))G(x)(modxn)g(x)=G(x)(1lnG(x)+f(x))(modxn)

然后就可以递推了。

多项式快速幂

g(x) 满足 g(x)fC(x)(modxn)

g(x)fC(x)(modxn)lng(x)Clnf(x)(modxn)g(x)eClnf(x)(modxn)

然后就是 exp。

多项式开根

g(x) 满足 gC(x)f(x)(modxn)

gC(x)f(x)0(modxn)

F(g(x))=gC(x)f(x),然后直接带入牛迭的式子:

g(x)=G(x)(GC(x)f(x))CGC1(x)(modxn)g(x)=(C1)G(x)C+f(x)CGC1(x)(modxn)

多项式快速幂。

多项式多点求值

给定 n,长度为 n 的多项式 f 以及数组 a,求所有 f(ai)

分治。

t=m2

g(x)=i=1t(xai),h(x)=i=t+1m(xai)

f(x)=g(x)A(x)+B(x)f(x)=h(x)C(x)+D(x)

可以发现,对于 1itf(i)=0A(i)+B(i)=B(i),对于 t+1imf(i)=0C(i)+D(i)

然后发现我们可以分治 NTT 预处理 g,h,所以递归时做一遍多项式取模即可,但常数巨大。

多项式快速插值

拉插的式子:

f=i=1nYijixXjXiXj=i=1nji(xXj)Yiji(XiXj)

发现后面 Yi 是一个常数,下面可以看做:

G(Xi)=limxXij(xXj)xXi

可以洛必达

=limxXi(j(xXj))

所以分治 NTT 后多点求值。

fl,r 表示 [l,r] 的答案。

fl,r=i=lrYiG(Xi)ji(xXj)=fl,midi=mid+1r(xXi)+fmid+1,ri=lmid(xXi)

GL&HF

目前就这些,有问题请指出,之后可能会再施工。

posted @   TOBapNw-cjn  阅读(101)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示