zoukankan      html  css  js  c++  java
  • 简单多项式学习笔记

    离谱的多项式

    ——简单多项式和生成函数学习笔记

    By Tuifei_oier

    Part 1 前言

    多项式和生成函数同属 OI 中数学方面的知识,在学习它们的过程中,不仅要掌握一些基础的对于它们本身的知识和操作,更要学会去通过构造和计算来解决相关的问题。
    因此,本文先从定义入手记录一些多项式和生成函数的基础操作,再记录一些经典的应用和构造方式。

    Part 2 科技篇

    随便编的定义

    首先,我们给出形式幂级数的定义:
    对于形式幂级数 (F(x)=sumlimits_{ige0}a_ix^i),我们不关心 (x) 的取值,也不关心它的收敛性等,仅仅关心它的各项系数。因此,对于形式幂级数,我们认为它可以进行任何变换而不受限制。例如:

    [sum_{ige0}x^i=dfrac{1}{1-x} ]

    一般来说,当 (xin(0,1)) 时左式收敛,于是等号成立。
    但是对于形式幂级数,由于我们不关心 (x) 的值也不关心求和式最终的结果,所以等号何时都成立。

    接下来,定义接下来讨论的多项式 (F(x)=sumlimits_{i=0}^na_ix^i),它是一个 (n)形式多项式,即系数数列有限项的形式幂级数。在 OI 中,我们大部分情况下讨论形式多项式,即我们同样不关心 (x) 的具体取值。

    一些表示:
    (F(x_0)) 表示 (x=x_0)(F(x)) 的取值。
    (F_i)([x^i]F(x)) 表示 (F(x))(x^i) 项的系数。

    多项式全家桶

    首先是最基础的卷积,即:

    [H_{i+j}=sum_{i=0}^nsum_{j=0}^mF_iG_j ]

    就是把两个多项式相乘,利用 FFT 可以 (O(nlog n)) 解决。


    一维循环卷积,即:

    [H_{x}=sum_{i+jequiv x}F_iG_j ]

    发现实际上 FFT 求的就是循环卷积,于是直接做之后把系数加到 (mod n) 后的位置上就好了。
    对于二维循环卷积,即:

    [H_{x,y}=sum_{i+jequiv x}sum_{k+lequiv y}F_{i,k}G_{j,l} ]

    我们可以先求出对于两边的矩阵做 DFT,然后对位相乘得到新矩阵再 IDFT 回去。
    如何 DFT 一个矩阵呢?我们可以先把每一行看成一个多项式系数数列,然后把它们代表的多项式都 DFT,对得到的新矩阵的每一列再进行相同操作,最后就得到了原矩阵的 DFT 矩阵,IDFT 就是把整个操作逆向进行即可。
    对于高维,同理。
    关于循环卷积有一个重要的算法——Bluestein 算法。
    因为在 DFT 和 IDFT 过程中都要求 (n)(2) 的若干次方;而当 (n) 不为 (2) 的次方时,我们要不一次一次暴力做,但这样可能会导致复杂度问题;或者,我们就可以利用这个算法。具体的说,对于长度为 (n) 的数列 (A),我们要求长度为 (n) 的数列 (B) 满足:

    [B_i=sum_{j=0}^{n-1}x^{ij}A_j ]

    [=sum_{j=0}^{n-1}x^{frac{i^2+j^2-(i-j)^2}{2}}A_j ]

    [=x^{frac{i^2}{2}}sum_{j=0}^{n-1}A_jx^{frac{j^2}{2}}x^{frac{-(i-j)^2}{2}} ]

    于是,我们设 (C_i=x^{frac{i^2}{2}}sumlimits_{j=0}^{i}A_jx^{frac{j^2}{2}}x^{frac{-(i-j-n)^2}{2}}),此时,由于 (forall xge n,A_x=0),所以 (B_i=C_{i+n}),而 (C_i) 就是一个卷积形式的式子了,我们就可以 (O(nlog n)) 求对于任意的 (n) 的 DFT 和 IDFT 了。


    多项式求逆,即给定 (n-1) 次多项式 (F(x)),求次数不超过 (n-1) 的多项式 (G(x)) 满足:

    [F(x)\,G(x)equiv1(mod x^n) ]

    如何求这个东西呢?我们考虑用倍增来求。
    假设我们当前已经知道 (G_n(x)) 使得 (F(x)\,G_n(x)equiv1(mod x^n)),要求 (G_{2n}(x)) 使得 (F(x)\,G_{2n}(x)equiv1(mod x^{2n}))
    则:

    [(F(x)G_n(x)-1)^2equiv0(mod x^{2n}) ]

    [F^2(x)G^2_n(x)+1-2F(x)G_n(x)equiv0pmod{x^{2n}} ]

    [2F(x)G_n(x)-F^2(x)G^2_n(x)-F(x)G_{2n}(x)equiv0pmod{x^{2n}} ]

    [G_{2n}(x)equiv2G_n(x)-F(x)G_n^2(x)pmod{x^{2n}} ]

    于是我们就可以倍增了,复杂度 (O(nlog n))


    多项式除法,即给定 (n) 次多项式 (F(x))(m) 次多项式 (G(x)),求一个 (n-m) 次多项式 (H(x)) 和一个次数小于 (m) 的多项式 (R(x)),使得:

    [F(x)=G(x)\,H(x)+R(x) ]

    如何求呢?我们考虑设 (F'(x)=F(dfrac{1}{x})x^n),即 (F(x))(0-n) 次项系数翻转,则(假设 (R(x)) 次数为 (m-1),不够则补 (0)):

    [F'(x)=G'(x)\,H'(x)+x^{n-m+1}R'(x) ]

    [F'(x)=G'(x)\,H'(x)pmod{x^{n-m+1}} ]

    (H'(x)) 的次数为 (n-m),所以我们在 (mod x^{n-m+1}) 意义下求出的 (H'(x)) 和真实的 (H'(x)) 相同。
    于是通过多项式求逆求出 (H'(x)),代回原式解出 (R(x))
    复杂度 (O(nlog n))


    多项式 ln,即给定多项式 (F(x)),求 (ln F(x)pmod{x^n})
    首先解释下为什么一个多项式可以 (ln)。从这里开始由于我们把多项式看成形式多项式,因此在把 (f(x)=ln x)(x_0=1) 处泰勒展开后得到的式子恰为一个关于 (x) 的幂级数,把 (x)(F(x)) 来代入就可以理解为 (ln F(x))
    接下来考虑怎么计算。
    (G(x)=ln F(x)pmod{x^n}),则:

    [G'(x)=dfrac{F'(x)}{F(x)}pmod{x^n} ]

    于是求个逆求个导卷起来再积分回去即可。
    常数项肯定为 (0)。(因为 (F(x)) 的常数项肯定为 (1)


    牛顿迭代。即对于一个函数(自变量任意)(G(x)),求多项式 (F(x)) 使得 (G(F(x))equiv0pmod{x^n})
    考虑倍增。假设我们知道 (G(F_n(x))equiv0pmod{x^n}),欲求 (F_{2n}(x)) 满足 (G(F_{2n}(x))equiv0pmod{x^{2n}})
    (G(x))(G(F_n(x))) 处作泰勒展开,得到:

    [G(x)=G(F_n(x))+G'(F_n(x))(x-F_n(x))^1/1!+G''(F_n(x))(x-F_n(x))^2/2!+... ]

    代入 (x=F_{2n}(x)),则

    [G(F_{2n}(x))=G(F_n(x))+G'(F_n(x))(F_{2n}(x)-F_n(x))^1/1!+G''(F_n(x))(F_{2n}(x)-F_n(x))^2/2!+... ]

    注意到 (F_{2n}(x))(F_n(x)) 的前 (n-1) 项应该相同,则 (F_{2n}(x)-F_n(x)equiv0pmod{x^n}),于是上式在 (pmod{x^{2n}}) 意义下为:

    [0equiv G(F_n(x))+G'(F_n(x))(F_{2n}(x)-F_n(x))pmod{x^{2n}} ]

    [F_{2n}(x)equiv F_n(x)-dfrac{G(F_n(x))}{G'(F_n(x))}pmod{x^{2n}} ]

    这样就可以倍增了。之前的一些科技也可以用这种牛顿迭代的形式来求(如求逆)。


    多项式 exp,即给定 (P(x)),求 (e^{P(x)}pmod{x^n})
    如何理解?(f(x)=e^x) 同样可以泰勒展开成幂级数。
    设函数 (G(x)=ln x-P(x)),利用刚才的牛顿迭代,得到:

    [F_{2n}(x)equiv F_n(x)(1-ln F_n(x)+P(x)) ]

    写完 (ln) 后倍增即可,复杂度 (O(nlog n)),注意常数项为 (1)


    多项式快速幂,即给定 (F(x)),求 (G(x)equiv F^k(x)pmod{x^n})
    首先可以和普通快速幂一样做,复杂度 (O(nlog^2n))
    考虑低一点的复杂度,我们先把 (F^(x))(ln),给系数都乘上 (k) 之后再 (exp) 回去。
    但有一点问题,就是 (ln)(exp) 均对常数项有要求。如何解决?提公因式即可,注意 (k) 的细节。
    复杂度 (O(nlog n))

    生成函数

    一般来说,对于数列 ({a_i}),我们有两种生成函数:

    1. OGF,一般生成函数,形如 (F(x)=sumlimits_{ige0}a_ix^i)
    2. EGF,指数生成函数,形如 (F(x)=sumlimits_{ige0}dfrac{a_i}{i!}x^i)

    先看一般生成函数,我们把一个数列写成这样的形式,就可以实现这样的效果:

    小例题:给定 (4) 种物品,每种物品都有无限个,其中,第一种物品只能选偶数个,第二种只能选 (5) 的倍数个,第三种物品最多选 (4) 个,第四种物品,最多选 (1) 个,求从中选出 (n) 个物品的方案数。

    考虑写出四种物品的拿取方案序列:
    ({1,0,1,0,...},{1,0,0,0,0,1,0,0,0,0,1,...},{1,1,1,1,1,0,0...},{1,1,0,0,...})
    即序列第 (i) 项(从 (0) 开始)表示对于该种物品拿 (i) 个的方案数。
    求出它们的生成函数,把它们乘在一起:

    [dfrac{1}{1-x^2}dfrac{1}{1-x^5}dfrac{x^5-1}{x-1}dfrac{x^2-1}{x-1} ]

    [=dfrac{1}{(1-x)^2} ]

    [=(1+x+x^2+x^3+...)^2 ]

    此时,所得多项式第 (n) 项的系数即为,答案,不难得到答案为 (n+1)

    这就是一般生成函数的妙用,两个生成函数的卷积可以理解为在做背包合并问题,方便计数。
    同时,假设我们要求一个数列的若干项,可以先写出这个数列的生成函数,然后通过某些计算算出这个生成函数的各项系数(比如利用多项式科技),这样一来就解决了问题。

    再看指数生成函数,为什么写成这样呢?考虑两个指数生成函数 (F(x),G(x)) 乘积:

    [H(x)=(sum_{ige0}dfrac{a_i}{i!}x^i)(sum_{jge0}dfrac{b_j}{j!}x^j) ]

    [=sum_{ige0}(sum_{j=0}^ia_jb_{i-j}C_i^j)dfrac{x^i}{i!} ]

    这样一来,乘在一起后系数不是 (a_jb_{i-j}),而是还有一个组合数 (C_i^j),就可以解决一些排列组合相关的问题。

    Part 3 应用篇

    接下来是一些上文的应用。
    实际上在 OI 中的应用还是以生成函数的形式为主,但也有利用卷积定义之类的题型。

    Pro A 第二类斯特林数 (Luogu 5395)

    给定 (n,m),求 (S(n,0),S(n,1),S(n,2),...,S(n,m)) 的值。
    (tips:1le mle nle 10^5)

    直接暴力算肯定不行,考虑如何优化。
    我们发现,把斯特林数写成通项形式:

    [S(n,m)=dfrac{sum_{i=0}^m(-1)^iC_m^i(m-i)^n}{m!} ]

    [=sum_{i=0}^m(-1)^idfrac{1}{i!}dfrac{1}{(m-i)!}(m-i)^n ]

    [=sum_{i=0}^mdfrac{(-1)^i}{i!}dfrac{(m-i)^n}{(m-i)!} ]

    于是,构造 (f(x)=sumlimits_{i=0}^mdfrac{(-1)^i}{i!}x^i,g(x)=sumlimits_{i=0}^mdfrac{i^n}{i!}x^i),则 (S(n,m)=[x^m](f*g))
    这就是一个经典的应用卷积定义的算法,复杂度 (O(nlog n))

    Pro B 付公主的背包(Luogu 4389)

    给定 (n) 个物品,每种物品的都有无数个,物品 (i) 的体积为 (v_i)
    求挑出若干个物品使得体积和为 (V) 的方案数。
    (tips:1le n,Vle 10^5)

    整个问题就是一个背包问题,考虑用一般生成函数来求方案数。
    则第 (i) 个物品的生成函数 (f_i(x)=sumlimits_{jge0}x^{v_ij}=dfrac{1}{1-x^{v_i}})
    于是我们要求答案数列的生成函数

    [G(x)=prod_{i=1}^nf_i(x)=prod_{i=1}^ndfrac{1}{1-x^{v_i}} ]

    [ln G(x)=sum_{i=1}^nlndfrac{1}{1-x^{v_i}} ]

    [=-sum_{i=1}^nln(1-x^{v_i}) ]

    [(ln G(x))'=-sum_{i=1}^ndfrac{-v_ix^{v_i-1}}{1-x^{v_i}} ]

    [=sum_{i=1}^nv_ix^{v_i-1}sum_{jge0}x^{v_ij} ]

    [=sum_{i=1}^nsum_{jge1}v_ix^{v_ij-1} ]

    再积回去,得到:

    [ln G(x)=sum_{i=1}^nsum_{jge1}dfrac{x^{v_ij}}{j} ]

    于是求出 (ln G(x)),再多项式 (exp) 即可。
    复杂度 (O(Vlog V)),注意在求 (ln G(x)) 时需要用到一些小技巧。

    Pro C 排列数 (Luogu 5824)

    给定 (n,m),求 (p_{n,m}) 的值。
    (tips:1le n,mle2 imes10^5),其中 (p_{x,y}) 表示将 (x) 写成 (y) 个自然数的和的方案数,不考虑顺序。
    例如,(5=(0+0+5)=(0+1+4)=(0+2+3)=(1+1+3)=(1+2+2)),因此 (p_{5,3} = 5)

    首先,我们有一个 (O(nm)) 的递推:

    [p_{i,j}=p_{i,j-1}+p_{i-j,j} ]

    考虑为什么,我们对于 (p_{i,j}),分别考虑 (j) 个自然数中是否存在 (0) 来转移。
    但这道题仅仅这样是不行的,考虑优化。
    我们设 (F_i(x)=sum_{jge0}p_{j,i}x^j),则:

    [F_i(x)=sum_{jge0}(p_{j,i-1}+p_{j-i,i})x^j ]

    [=F_{i-1}(x)+x^iF_i(x) ]

    于是移项得到:

    [F_i(x)=dfrac{F_{i-1}(x)}{1-x^i} ]

    (dp_{i,0}=[i=0]),于是 (F_0(x)=1),所以:

    [F_i(x)=prod_{j=1}^idfrac{1}{1-x^j} ]

    发现了什么?这玩意跟上一题的式子几乎一模一样。
    于是直接一样的处理就行了,复杂度 (O(nlog n))

    这道题属于一类经典问题,通过构造答案数列的生成函数,利用递推式等关系来得到生成函数的关系并解方程,最后就可以得到答案了。

    Pro D 数学竞赛 (Luogu 5517)

    给定一个长度为 (2^{64}) 的数列 ({a_n}),其中 (a_0=-3,a_1=-6,a_2=-12,a_n=3a_{n-1}+a_{n-2}-3a_{n-3}+3^n)
    (T) 组询问,每次给定 (n),求 (a_npmod{p=10^9+7})
    (tips:1le Tle5 imes10^7,0le nle2^{64}-1)

    介绍一种比较生成函数的做法:
    考虑利用刚才的想法,设 (F(x)=sum_{ige0}a_ix^i),则:

    [F(x)=sum_{ige0}(3a_{i-1}+a_{i-2}-3a_{i-3}+3^i)x^i ]

    解方程得到:

    [F(x)=dfrac{12x-3}{(1-3x)^2(1+x)(1-x)} ]

    此时,对于这种分母为乘积的形式,一种比较通用的方法是待定系数法,即:

    [F(x)=dfrac{A}{(1-3x)^2}+dfrac{B}{1+x}+dfrac{C}{1-x}+dfrac{D}{1-3x} ]

    此时,(A,B,C,D) 可以解出来(参见原题解),然后就把它展开成幂级数形式,最后就得到答案了。
    复杂度可以做到 (O(sqrt p-T))

    Pro E 贝尔数 (Luogu 5748)

    定义贝尔数 (B_i=sumlimits_{j=0}^iS(i,j))(T) 组询问,求 (B_n)
    (tips:1le Tle1000,1le nle10^5)

    考虑贝尔数 (B_n) 的组合意义,等于是把 (n) 个带放入 (n) 个无标号的可空集合中的方案数。
    既然要利用上面的想法,我们就应该尝试去找贝尔数的递推公式,发现:

    [B_n=sum_{j=0}^{n-1}inom{n-1}{j}B_{n-j-1} ]

    即枚举和 (n) 在一个集合的是哪些数。

    [B_{n+1}=sum_{j=0}^ninom{n}{j}B_{n-j} ]

    发现相当于是做卷积的过程中增加了一项系数 (inom{n}{j}),这启示我们使用指数生成函数。
    (F(x)=sumlimits_{jge0}dfrac{B_j}{j!}x^j,F'(x)=sumlimits_{jge0}dfrac{B_{j+1}}{j!}x^j),于是 (F'(x)=F(x)e^x)
    两边求导移项后得到:

    [B(x)=e^{e^x+c} ]

    代入 (B(0)=1) 得到 (c=-1),于是 (B(x)=e^{e^x-1})
    复杂度 (O(nlog n-T))

    Part 4 小结

    生成函数和多项式作为近些年新兴的出题方向,都透露了极强的数学性,要求 oier 有较高的数学计算能力和思维力,同时又可以方便地结合类似递推之类的内容,因此有很有必要好好掌握。
    通过这样的小结形式,积累下来的 trick 或许会有大用吧。

  • 相关阅读:
    封装
    面向对象的思想
    Arrays工具类
    二分查找
    选择排序
    冒泡排序
    对象数组
    二维数组
    一维数组
    循环语句注意事项
  • 原文地址:https://www.cnblogs.com/tuifei-oiers-home/p/14226124.html
Copyright © 2011-2022 走看看