zoukankan      html  css  js  c++  java
  • 多项式:从什么都不知道到门都没入

    多项式

    单项式:由数字和字母组成的积的代数式称为单项式

    多项式:由若干个单项式相加组成的代数式叫做多项式

    废话,这上过初中的人都知道

    一、一元多项式

    (nin N^*),则我们称多项式

    [a_nx^n+a_{n-1}x^{n-1}+...+a_1x+a_0 ]

    一元多项式

    其中,我们称(a_ix^i)(i)次项,(a_i)(i)次项系数。

    两个多项式(f(x))(g(x))相等,当且仅当它们同次项的系数相等。

    (a_n ot= 0),则我们称(a_n)为多项式的首项,(n)为多项式的次数,记为(deg) (f)

    二、 多项式的基本运算

    1.加法与乘法运算

    [f(x)=a_nx^n+a_{n-1}x^{n-1}+...+a_1x+a_0\ g(x)=b_nx^n+b_{n-1}x^{n-1}+...+b_1x+b_0 ]

    [f(x)+g(x)=(a_n+b_n)x^n+(a_{n-1}+b_{n-1})x^{n-1}+...+(a_1+b_1)x+a_0+b_0\ f(x) imes g(x)=a_nb_nx^{2n}+...+sum_{i=0}^n a_{n-i}b_i x^n+...+a_1b_1x+a_0b_0 ]

    多项式的加法和乘法满足交换律和结合律,乘法还满足分配律

    2.带余除法

    (f(x))(g(x))是的两个多项式,且(g(x))不等于0,则有唯一的多项式 (q(x))(r(x)),满足

    [f(x)=q(x)g(x)+r(x) ]

    其中(r(x))的次数小于(g(x))的次数。

    此时,

    (q(x)) 称为(g(x))(ƒ(x))的商式。

    (r(x))称为余式。

    (r(x)=0),则我们称(g(x))(f(x))的因式

    多项式的带余除法不是本文研究的重点,有兴趣的可以自行查阅资料

    三、关于多项式的几个重要定理

    1.代数基本定理

    任何复系数一元n次多项式方程在复数域上至少有一根。

    代数基本定理在数学中的运用十分广泛。

    2.唯一分解定理

    数域(P)上的每个次数大于零的多项式(f(x))都可以分解为数域(P)上的不可约多项式的乘积

    这与算术基本定理十分相似。

    3.高斯引理

    本原多项式:若多项式(a_nx^n+a_{n-1}x^{n-1}+...+a_1x+a_0)满足(gcd(a_1,a_2,...,a_n)=1),则我们称该多项式为本原多项式

    高斯引理: 两个本原多项式的积是本原多项式

    这些引理的应用是数学竞赛的范围了,有兴趣的可以自行了解。

    四、多项式的零点

    若有一数(a)使得多项式(f(x)=0),则我们称(a)为多项式(x)的零点。

    这里我们有两个重要的定理:

    1.余式定理

    (f(x))除以(x-a)的余式是(f(a))

    证明:
    (g(x))为商式,(r(x))为余式,则我们有:

    [f(x)=(x-a)g(x)+r(x) ]

    (x=a)代入得:

    [f(a)=(a-a)g(a)+r(a)=r(a) ]

    证毕。

    余式定理的几个推论:

    1.因式定理

    如果多项式(f(a)=0),那么多项式(f(x))必定含有因式(x-a)

    2.多项式(a_nx^n+a_{n-1}x^{n-1}+...+a_1x+a_0)至多有(n)个不同的零点。

    用反证法可以证明,这里略去。

    3.如果多项式(a_nx^n+a_{n-1}x^{n-1}+...+a_1x+a_0)至少有(n+1)个零点,那么该多项式为(0)

    如果此时他不是(0),则他至多有(n)个零点,由(2)导出矛盾。

    2.恒等定理

    若有无穷多个(a)使得(f(a)=g(a)),则(f(x)=g(x))

    (h(x)=f(x)-g(x)),因为此时(h(x))有无穷多个零点,由上面的第3个结论可知,(h(x)=0)

    看到这里,可能很多人不耐烦了:

    我们是OIer,你给我们讲那么多这些玩意干什么?

    如果上面是纯数学竞赛内容的话,那么下面就是数竞与信竞的交集了。

    --------------------------------------------------------------分割线------------------------------------------------------------------------------------

    五、多项式的插值与差分

    (注意:本章将涉及到一些OI中的知识,数竞党看不懂可略过)

    我们回到多项式乘法。

    如果我们暴力乘两个(n)次多项式,那么时间复杂度将是(O(n^2))

    那么我们可不可以对一般的多项式乘法进行些优化呢?

    我们可以通过改变运算顺序(CPU的运算原理相关)来优化,但我们最多只能优化些常数因子罢了

    如果我们不能跳出上面的思维局限,那么我们不可能在根本上优化算法的时间复杂度

    我们得换个角度观察问题。

    在平面直角坐标系中,我们给出平面上(n)个不同的点:

    [(x_1,y_1),(x_2,y_2),...,(x_n,y_n) ]

    那么,我们能否找到一条曲线,使得该曲线穿过这些点呢?

    换言之,我们希望找到一个函数(f(x)),使得:

    [f(x_1)=y_1\ f(x_2)=y_2\ ...\ f(x_n)=y_n ]

    但显然,这样的函数有无数个。

    一般而言,多项式函数是这些函数中最简单的。

    那么,多项式就有另外一种表示法:

    多项式的点值表示法

    我们用满足(f(x)=y)(n)个数对

    [(x_1,y_1),(x_2,y_2),...,(x_n,y_n) ]

    来表示一个(n-1)次多项式。

    如果我们给定一个多项式,给出(n)组点值最简单的方法就是任选(n)个不同的(x_i),分别计算此时多项式的值

    (n)个点值对也可以唯一确定一个不超过(n-1)次多项式,这个过程称为插值

    于是,一个著名的公式出现了:

    拉格朗日插值公式

    对于多项式的点值

    [(x_0,y_0),(x_1,y_1),...,(x_{n-1},y_{n-1}) ]

    存在一个唯一的不超过(n-1)次的多项式(f(x)),并且(f(x))可以表示为:

    [f(x)=y_0frac{(x-x_1)(x-x_2)...(x-x_{n-1})}{(x_0-x_1)(x_0-x_2)...(x_0-x_{n-1})}\ +y_1frac{(x-x_0)(x-x_2)...(x-x_{n-1})}{(x_1-x_0)(x_1-x_2)...(x_2-x_{n-1})}+...\ +y_{n-1}frac{(x-x_0)(x-x_1)...(x-x_{n-2})}{(x_{n-1}-x_0)(x_{n-1}-x_1)...(x_{n-1}-x_{n-2})} ]

    整理的好看点:

    [f(x)=sum_{i=0}^{n-1} y_i imes prod_{0leq i ot=jleq n-1} frac{x-x_j}{x_i-x_j} ]

    为了证明插值公式,我们必须证明多项式插值的存在性和唯一性

    存在性证明:

    (这部分涉及一些线性代数的知识,如果实在不会可以略过)

    (估计也只有我这种蒟蒻不怎么会线性代数)

    我们可以把多项式的每个点值看成一个方程,那么我们就有了(n)个方程,它们组成了线性方程组。

    我们把这个线性方程组改写成矩阵的形式:

    [egin{bmatrix} 1&x_0& x_0^2 &cdots & x_0^{n-1} \ 1& x_1& x_1^2 &cdots & x_1^{n-1}\ vdots& vdots& vdots & ddots & vdots\ 1& x_{n-1} & x_{n-1}^2& cdots & x_{n-1}^{n-1} end{bmatrix} egin{bmatrix} a_0\ a_1\ vdots\ a_{n-1} end{bmatrix}= egin{bmatrix} y_0 \ y_1 \ vdots \ y_{n-1} end{bmatrix} ]

    我们不妨记为(XA=Y)

    该式可以转化为(A=X^{-1}Y)

    因此我们只需要证明(X)有没有逆矩阵即可。

    Q:什么是逆矩阵啊

    A:若我们有(AB=E)(E)为单位矩阵),则我们称(B)(A)矩阵的逆

    Q:单位矩阵又是啥啊

    A:主对角线上全是1的举证就是单位矩阵。单位矩阵有一个性质,即任何矩阵乘单位矩阵都等于它本身

    这里我们介绍一个东西:范德蒙行列式

    线代julao可略过

    长成上面这样的矩阵就是范德蒙矩阵了。

    而范德蒙行列式是有它的特殊的计算公式的。

    对于一个范德蒙矩阵,它的行列式的计算公式为:

    [det(X)=prod _{1leq i<jleq n} (x_j-x_i) ]

    显然,(x_j-x_i ot=0),所以矩阵的行列式值不等于0,即该矩阵有逆

    唯一性证明:

    如果有两个这样的多项式,那么它们的差就至少有(n)个不同的零点(点值中(y)值差为0),由上面余式定理的第三个推论可得,这个差值一定为0

    正确性证明:

    现在我们来看看拉格朗日插值公式是怎么构造这个多项式的。

    在特殊情形(y_1=y_2=...=y_{n-1}=0)时,求出满足条件多项式(f_1(x))。这时,(f_1(x))(n-1)个不同的零点(x_1,x_2,...,x_{n-1}),根据因式定理,该多项式被((x-x_1)(x-x_2)...(x-x_{n-1}))整除。

    我们有要求多项式的次数尽可能的低,故我们得到:

    [f_1(x)=c(x-x_1)(x-x_2)...(x-x_{n-1}) ]

    其中(c)是一个常数,由(f_1(x_0)=y_0)确定。

    我们就有:

    [f_1(x_0)=y_0=c(x_0-x_1)(x_0-x_2)...(x_0-x_{n-1}) ]

    所以

    [c=frac{y_0}{(x_0-x_1)(x_0-x_2)...(x_0-x_{n-1})} ]

    于是

    [f_1(x)=y_0frac{(x-x_1)(x-x_2)...(x-x_{n-1})}{(x_0-x_1)(x_0-x_2)...(x_0-x_{n-1})} ]

    类似的,我们可以构造(f_i(x)),使得(f_i(x_i)=y_i)

    显然,当(x ot=i)时((x)(x_0,x_1,...x_{n-1})中的任意一个),(f_i(x)=0)(因为分子总有一个因式值为0)

    所以,这样的(f(x)=f_1(x)+f_2(x)+...f_n(x))满足条件。

    优缺点

    拉格朗日插值公式结构紧凑,十分优美,易于理解,在理论分析中十分方便。

    然而,当一个插值点改变时,整个插值公式必须要重新计算。

    解决方案就是使用重心拉格朗日插值法或者牛顿迭代法。

    然而我太菜了,这两个方法我都不会。

    还是得多多学习啊!

    插值介绍完了,现在轮到差分了。

    广大OIer对差分的影响,估计是这样的:

    差分不就是和前缀和结合在一起用的东西吗!

    树状数组区间修改用差分!(注:局限性很大)

    树上差分!天天爱跑步!(明明线段树合并就解决了

    多项式差分正是差分众多形式中的一种。

    对于函数(f(x))和固定的(h ot=0)

    [f(x+h)-f(x) ]

    称为(f(x))步长为(h)的一阶差分,记作(Delta_h^1f(x))(Delta_h^1f(x))的差分

    [egin{align} Delta_h^1f(x+h)-Delta_h^1f(x)&=(f(x+2h)-f(x+h))-(f(x+h)-f(x))\ &=f(x+2h)-2f(x+h)+f(x) end{align} ]

    称为(f(x))的(步长为(h))的二阶差分,记作(Delta_h ^2f(x))

    对于(f(x))(n)阶差分,我们有以下公式:

    [Delta _h ^n f(x)=sum_{i=0}^n(-1)^{n-i}C_n^if(x+ih) ]

    上述公式在(n=1)时显然成立,于是我们可以结合数学归纳法证明,此处略去。

    讲了这么多,一点代码也没有,总该来道题吧!

    于是模板题来了!(数竞党慎入)

    P4781【模板】拉格朗日插值法

    直接上代码吧:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2005;
    const int MOD=998244353;
    typedef long long ll;
    int n,k;
    ll x[maxn];
    ll y[maxn];
    inline ll ksm(ll x,int y){
    	if(!y) return 1;
    	if(y==1) return x%MOD;
    	ll tmp=ksm(x,y/2);
    	tmp=(tmp*tmp)%MOD;
    	if(y&1) return (tmp*x)%MOD;
    	else return tmp; 
    }
    int main(){
    	scanf("%d%d",&n,&k);
    	for(int i=1;i<=n;i++) scanf("%lld%lld",&x[i],&y[i]);
    	ll ans=0;
    	for(int i=1;i<=n;i++){
    		ll s1=y[i]%MOD; 
    		ll s2=1;
    		for(int j=1;j<=n;j++) if(i!=j){
    			s1=(s1*(k-x[j]))%MOD;
    			s1=(s1+MOD)%MOD;
    			s2=(s2*(x[i]-x[j]))%MOD;
    			s2=(s2+MOD)%MOD;
    		}
    		ans=(ans+(s1*ksm(s2,MOD-2))%MOD)%MOD;
    		ans=(ans+MOD)%MOD;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    六、多项式乘法的优化

    我们先前讨论过多项式的点值表示法。

    普通的多项式乘法是(O(n^2))的,但如果我们换成点值表示法呢?

    现在我们有两个多项式(f(x))(g(x)),记它们的积为(r(x))

    则对于同样的(a),它们分别有点值((a,y_f))((a,y_g))((a,y_r))

    显然,(y_r=y_f*y_g)

    所以如果我们对于每个参与乘法的多项式有(n)个点值的话,我们可以在(O(n))的时间内完成乘法运算!

    于是,对于系数表示法的多项式的乘法运算,我们可以尝试用点值表示法优化。

    过程如下:

    1.将系数表达法的多项式改写为点值表示法的多项式(点值过程

    2.用点值表示法计算多项式的乘法。

    3.将点值表示法转化为系数表示法。(插值过程

    下面,我们重点介绍完成这一过程的算法。

    七、DFT,IDFT与FFT

    DFT:离散傅里叶变换,用来完成点值过程。

    IDFT:离散反傅里叶变换,用来完成插值过程。

    FFT:DFT和IDFT的高效算法。

    前置知识

    1.复数

    我们把形如(a+bi(a,bin R))的数称为复数,其中(i)为虚数单位,规定

    [i^2=-1 ]

    其中(a)被称为实部,(b)被称为虚部。

    知道这玩意有啥用?(我还是个初中的蒟蒻

    一会你就知道了。

    我们大家都知道笛卡尔发明的万恶的平面直角坐标系。

    对于复数,我们有一个类似的东西:复平面

    u=3797762356,1963283957&fm=26&gp=0

    复平面的横轴是实数轴,纵轴是虚数轴。

    于是对于每一个复数,我们都有一个唯一对应的向量了。

    我们可以用((r, heta))来表示一个虚数,其中(r)为虚数的长度,( heta)为该向量与(x)轴正半轴的夹角。

    复数也是有它的四则运算的。

    对于两个复数(z_1=a+bi)(z_2=c+di),它们的和 和 积为:

    [z_1+z_2=(a+c)+(b+d)i\ z_1 imes z_2=(a+bi)(c+di)=(ac-bd)+(ad+bc)i ]

    复数相加和向量加法一样,满足平行四边形法则

    对于乘法,如果用坐标表示的话,我们有:

    [(r_1, heta_1) imes(r_2, heta_2)=(r_1 imes r_2, heta_1+ heta_2) ]

    即传说中的模长相乘,辐角相加

    所以如果两个复数的长度都是1,那他们的乘法就知识单纯的角度相加了。

    2.单位根

    对于我们的点值过程,我们当然可以随便取几个值代入去算。

    但显然,暴力计算(x,x^2,x^3...,x^n)当然是(O(n^2))的。

    我们可不可以不计算这些呢?

    我们当然希望这些数是(1,-1)这样的数,因为它们的若干次方是1。但明显,这不够。

    傅里叶:这个圆上的每个点都可以。捕获

    没错,这个圆是单位圆

    我们可以把这个圆(n)注意:(n)是2的幂次)等分,从((1,0))开始,编号为(0)(n-1)。我们记编号为(k)的点为(w_n^k)

    显然,((w_n^1)^k=w_n^k(k ot=0))(模长相乘,辐角相加)。

    我们把(w_n^1)称为(n)次单位根,简单记为(w_n)

    而这些(w_n^0,w_n^1,w_n^2,...,w_n^{n-1})有个极其重要的性质:它们的(n)次方等于1!

    下面我们就要来证明这个性质:

    对于(w_n^0),它恒等于1。

    而对于大于0的正整数(k)而言,(w^k_n=(w^1_n)^k)

    所以只要证明(w_n)(n)次方等于1即可。

    由于我们是把单位圆(n)等分,我们很容易得到(注意这是单位圆):

    [w_n=cos frac{2pi}{n}+isin frac{2pi}{n} ]

    由欧拉公式

    [e^{ix}=cos x+isin x ]

    (x)(frac{2pi}{n})

    [w_n=e^{frac{2pi i}{n}} ]

    所以

    [(w_n)^n=(e^{frac{2pi i}n{}})^n=e^{2pi i} ]

    欧拉公式中的(x)(2pi)

    [e^{2pi i}=cos 2pi +i sin 2pi =1 ]

    所以

    [(w_n)^n=1 ]

    证毕。

    单位根还有以下两个性质:

    1.(w_{2n}^{2k}=w_n^k)

    它们表示的复数(向量)是相同的,可以感性理解以下。

    2.(w_n^{k}=-w_{n}^{k+frac{n}{2}})

    它们表示的向量等大而且反向((frac{n}{2})表示二分之一个圆周)

    上面两个性质可以结合欧拉公式给出证明,但我觉得证明了反而不是那么好理解了。

    快速傅里叶变换(FFT)

    有了单位根,我们就可以在DFT的过程中减少次方运算了。

    但对于不是(2)的幂次的项,还是要我们暴力乘出来,总体的时间复杂度依然是(O(n^2))的。

    这时,FFT闪亮登场!

    第一步:离散傅里叶变换(DFT)

    现在我们有一个(n)项的多项式(同样地,(n)是2的幂次,且这里(nge2)):

    [f(x)=a_0+a_1x+a_2x^2+...+a_{n-1}x^{n-1} ]

    我们现在根据奇偶性把这个多项式分为两半:

    [f(x)=(a_0+a_2x^2+...+a_{n-2}x^{n-2})+(a_1x+a_3x^3+...+a_{n-1}x^{n-1})\ =(a_0+a_2x^2+...+a_{n-2}x^{n-2})+x(a_1+a_3x^2+...+a_{n-1}x^{n-2}) ]

    我们再令:

    [g(x)=a_0+a_2x+a_4x^2+...+a_{n-2}x^{frac{n}{2}-1}\ r(x)=a_1+a_3x+a_5x^2+...+a_{n-1}x^{frac{n}{2}-1} ]

    (x^2)代入(g(x),r(x))

    [g(x^2)=a_0+a_2x^2+a_4x^4+...+a_{n-2}x^{n-2}\ r(x^2)=a_1+a_3x^2+a_5x^4+...+a_{n-1}x^{n-2} ]

    所以

    [f(x)=g(x^2)+x imes r(x^2) ]

    我们设(k<frac{n}{2}),代入(w_n^k)得:

    [f(w_n^k)=g((w_n^k)^2)+w_n^k imes r((w_n^k)^2)\ =g(w_n^{2k})+w_n^k imes r(w_n^{2k})\= g(w_{frac{n}{2}}^k)+w_n^k imes r(w_{frac{n}{2}}^k) ]

    我们再代入(w_n^{k+frac{n}{2}})

    [f(w_n^{k+frac{n}{2}})=g((w_n^{k+frac{n}{2}})^2)+w_n^{k+frac{n}{2}} imes r((w_n^{k+frac{n}{2}})^2)\ =g(w_n^{2k+n})+w_n^{k+frac{n}{2}} imes r(w_n^{2k+n})\ =g(w_n^{2k}w_n^n)+w_n^{k+frac{n}{2}} imes r(w_n^{2k}w_n^n)\ =g(w_{frac{n}{2}}^k)-w_n^k imes r(w_{frac{n}{2}}^k) ]

    以上两式常被称为蝴蝶操作

    我们于是得到了一个结论:

    如果我们知道(g(x))(r(x))(w_{frac{n}{2}}^0,w_{frac{n}{2}}^1,...,w_{frac{n}{2}}^{n-1})处的值,那么我们就可以在线性时间内求出(f(x))(w_n^0,w_n^1,...,w_n^{n-1})处的值。

    我们再对(g(x))(r(x))进行相同的操作,于是我们就可以这么递归分治向下求解了。

    注意:我们只能对长度为(2)的次幂的数列进行分治FFT,否则递归向下的话两边长度就不相等了。

    一般对于这种情况,我们可以在第一次DFT之前把序列长度补成2的次幂。对于新补的高次项,我们令他们的系数为0。

    这样,算法的时间复杂度就是(O(nlog n))了。

    但是,我相信大家都知道,递归过程是很耗内存的。

    我们能不能不递归就进行FFT呢?

    当然可以!

    我们完全可以在原序列里把这些系数拆分了,然后在“倍增”地合并。

    现在的关键问题就是我们如何拆分这些系数了。

    这里我盗张图

    捕获2

    有啥规律呢?

    对于原序列中的系数(a_i),令(i)做二进制位翻转操作后的结果为(rev(i))(比如说:在长度为8的情况下,1就是001,翻转之后就是100,即4),那么(a_i)最后的位置就是(rev(i))

    第二步:离散反傅里叶变换(IDFT)

    IDFT的过程的作用我们先前说过了,就是要完成插值过程。

    我们把单位根代入,就得到下面的线性方程组的等效矩阵:

    [egin{bmatrix} 1& 1 &1 &cdots& 1\ 1& w_n^1& w_n^2&cdots &w_n^n\ 1& w_n^2& w_n^4&cdots &w_n^{2n}\ vdots& vdots &vdots &ddots &vdots \ 1& w_n^{n-1}&w_n^{2n-2}&cdots & w_n^{(n-1)^2} end{bmatrix} egin{bmatrix} a_0\ a_1\ a_2\ vdots\ a_{n-1} end{bmatrix} = egin{bmatrix} y_0\ y_1\ y_2\ vdots\ y_{n-1} end{bmatrix} ]

    我们仍然记为(XA=Y)

    相当于我们现在知道了(X,Y)去求(A)了。

    我们前面在探究多项式插值的时候,我们已经说明了多项式插值的存在性与唯一性(忘了可以重新看一眼)。

    现在,(A=X^{-1}Y),我们前面已经说明(X)存在逆矩阵,于是问题的关键就是求这个逆矩阵了。

    我们知道,(X)是范德蒙矩阵,它的行列式有它的特殊计算方式,而它的逆矩阵也有他独特的性质,就是每一项取倒数,再除以(n)(为什么是这样,我不会证

    我们在先前探讨单位根的时候,我们知道:

    [w_n=e^{frac{2pi i}{n}} ]

    我们现在取倒数,即-1次方:

    [w_n^{-1}=e^{frac{-2pi i}{n}} ]

    于是我们只要把(pi)取成(-3.1415926...),然后其他过程和DFT是一样的!

    所以,我们只需要一个函数,就可以完成DFT与IDFT两个操作了。

    好了,放上一道模板题:P3803 【模板】多项式乘法(FFT)

    Code:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e6+5;
    const double Pi=acos(-1.0);
    struct Complex{
    	double x,y;
    	Complex(double a=0,double b=0){
    		x=a; y=b;
    	}
    	Complex operator +(const Complex a)const{
    		return Complex(x+a.x,y+a.y);
    	} 
    	Complex operator *(const Complex a)const{
    		return Complex(x*a.x-y*a.y,x*a.y+y*a.x);
    	}
    	Complex operator -(const Complex a)const{
    		return Complex(x-a.x,y-a.y);
    	}
    }a[maxn<<1],b[maxn<<1];
    int n,m;
    int len;
    int r[maxn<<1];
    inline int read(){
    	char ch=getchar(); int sum=0; int f=1;
    	while(!isdigit(ch)) f=ch=='-'?-1:1,ch=getchar();
    	while(isdigit(ch)) sum=sum*10+ch-'0',ch=getchar();
    	return sum*f; 
    }
    inline void FFT(Complex *a,int len,int on){
    	for(int i=0;i<len;i++)
    		if(i<r[i]) swap(a[i],a[r[i]]);
    	for(int i=2;i<=len;i<<=1){//一次合并的长度
    		Complex wn(double(cos(2*Pi/i)),double(sin(on*2*Pi/i)));//当前单位根
    		for(int j=0;j<len;j+=i){//对每一块分别计算
    			Complex w(1,0);
    			for(int k=j;k<j+i/2;k++){//蝴蝶操作,计算当前多项式的点值
    				Complex u=a[k];
    				Complex t=w*a[k+i/2];
    				a[k]=u+t;
    				a[k+i/2]=u-t;
    				w=wn*w;
    			}
    		}
    	}
    	if(on==-1) for(int i=0;i<len;i++) a[i].x/=len; 
    }
    int main(){
    	n=read(); m=read();
    	for(int i=0;i<=n;i++) a[i].x=read();
    	for(int i=0;i<=m;i++) b[i].x=read();
    	len=1; int cnt=0;
    	while(len<=n+m) len<<=1,cnt++;//补成2^k长度
    	for(int i=0;i<len;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(cnt-1));//找到对应位置
    	FFT(a,len,1);//DFT
    	FFT(b,len,1);
    	for(int i=0;i<len;i++) a[i]=a[i]*b[i];//多项式乘法(用点值计算)
    	FFT(a,len,-1);//IDFT
    	for(int i=0;i<=n+m;i++) printf("%d ",int(a[i].x+0.5));//注意加0.5,防止被卡精度
    	printf("
    ");
    	return 0;
    } 
    

    我们这是“多项式入门”,也就入门到这了!

    多项式还有很多内容,包括:

    • 快速数论变换(NTT)
    • 快速沃尔什变换(FWT)
    • 多项式求逆
    • 多项式开方
    • 多项式除法
    • 多项式取模
    • 多项式的对数函数与指数函数
    • 多项式的三角函数与反三角函数
    • 多项式牛顿迭代
    • 多项式快速插值|多点求值

    这些已经超过我的能力范围(我毕竟还只是一个初中的蒟蒻OIer)。

    如果以后有机会的话,我会更新一个叫做多项式进阶的博客的!

    如有不足之处请指正,谢谢!

    参考资料:

    1.苏科版七年级上册《数学》教科书

    2.2002年国家集训队论文 张家琳《多项式乘法》

    3.2016年国家集训队论文 毛啸 《再探快速傅里叶变换》

    3.华东师范大学出版社《奥数教程》高中第三分册

    4.百度百科 范德蒙行列式

    5.OI wiki 快速傅里叶变换

  • 相关阅读:
    sql 事务
    GridView数据导入Excel
    图片对比度亮度调节函数
    在.NET(C#)中获取电脑名IP地址及当前用户名
    一个简单的存储过程
    通用海量数据库翻页
    Graphics
    sql 触发器
    DataTable中的数据导出Excel文件
    窗口渐变
  • 原文地址:https://www.cnblogs.com/ybwowen/p/11153817.html
Copyright © 2011-2022 走看看