多项式操作汇总

多项式操作汇总

最近学了不少多项式的小 trick,感觉不记一下容易忘。那我就把做法都摆在这里吧,代码还是自己去写了。(鉴于所有操作都是建立在 FFT 之上的,所以会写 FFT 的话所有东西的代码都能写出来了

多项式乘法

这个就是一切的基础,先把这个代码搞熟后面就都能写啦。

多项式逆元

定义多项式 \(g(x)\) 的逆元 \(g^{-1}(x)\)(以下为了方便简记为 \(f(x)\))是满足如下等式的多项式:

\[
\begin{matrix}
g(x) \cdot f(x) \equiv 1 & (\mod x^n)
\end{matrix}
\]

可以想象,模 \(x^n\) 的意思就是把 \(x^{n+1}\) 及更高的项都删掉,剩下的部分。为什么我们可以这样做呢?因为在解决实际问题的时候我们都只关心前 \(n\) 项(即 \(x^0 \sim x^{n-1}\) 的系数)。

求 \(f(x)\) 的方法可能不止一种,但是我觉得用泰勒展开这个方法虽然麻烦但是好记(因为大多数问题可以用它——它是“通解”)。

注意:以下多项式都会在第一次用 \(f(x)\) 表示过后就可能省掉后面的 \((x)\),你需要知道它是一个多项式。

我们先构造一个函数 \(H(f) = f^{-1} - g\)。你没有看错,这个 \(H(f)\) 函数的自变量就是一个多项式。为什么要这样构造函数呢?因为可以发现当自变量 \(f = g^{-1}\),即 \(f\) 就是答案的时候,\(H(f) = 0\)。

我们用倍增的方法求这个 \(f\),假设已经求出 \(f_0(x)\) 是 \(f\) 前 \(\lceil \frac{n}{2} \rceil\) 项,即

\[
\begin{matrix}
f_0 \equiv f & (\mod x^{\lceil \frac{n}{2} \rceil})
\end{matrix}
\]

现在我们要用 \(f_0\) 和 \(g\) 表示出 \(f\)。利用我们刚才构造的函数,根据 \(f_0\) 的定义有

\[
\begin{matrix}
H(f_0) \equiv 0 & (\mod x^{\lceil \frac{n}{2} \rceil})
\end{matrix}
\]

为了实现上面黑体字的目的,我们对 \(H(f)\) 在 \(f_0\) 处进行泰勒展开(想点进去看的请翻到文章末尾),即用下面这个式子去逼近 \(H(f)\)

\[
H(f) = \sum_ {i = 0}^{+ \infty} { \frac{H^{[i]}(f_0) (f-f_0)^i}{i!} }
\]

注意上面这个等式不是模意义下的,不需要取模它们就相等。

接下来我们观察一下 \((f-f_0)^i\),由于 \(f - f_0 \equiv 0 (\mod x^{\lceil \frac{n}{2} \rceil})\),发现当 \(i = 2\) 时,最高次项平方之后次数一定 \(\ge n\),所以有 \(\forall i > 1, (f-f_0)^i \equiv 0 (\mod x^n)\),那么上面的泰勒展开的无穷多项就只剩了两项,整理出来就是

\[
\begin{matrix}
H(f) \equiv H(f_0) + H‘(f_0)(f - f_0) & (\mod x^n) \\\\because H(f) \equiv 0 & (\mod x^n) \\\\therefore H(f_0) + H‘(f_0)(f - f_0) \equiv 0 & (\mod x^n)
\end{matrix}
\]

移项得到

\[
\begin{matrix}
f \equiv f_0 - \frac{H(f_0)}{H‘(f_0)} & (\mod x^n)
\end{matrix}
\]

至此我们得到了牛顿迭代的公式,以后可以记它,就不用每次都泰勒展开了。接下来的工作就是把刚才构造的 \(H(f)\) 带进去。

\[
\begin{matrix}
f \equiv 2f_0 - gf_0 & (\mod x^n)
\end{matrix}
\]

递归下去,再用这个式子回溯,就求出来啦!

时间复杂度:\(T(n) = T(\frac{n}{2}) + O(n\log n) = O(n\log n)\),发现这个小秘密后,我们可以无限套,理论复杂度不变

多项式除法 & 取模

注意这里的除法不是在模意义下的!所以它会有商和余数,即如果我要求 \(\frac{A(x)}{B(x)}\),我就是要求两个多项式 \(D(x)\) 和 \(R(x)\),使得它们满足下面这样一个等式

\[
A(x) = B(x)D(x) + R(x)
\]

现在我们记 \(n\) 为 \(A\) 的次数,\(m\) 为 \(B\) 的次数,那么有 \(D\) 的次数为 \(\mathrm{max}(n - m, 0)\),\(R\) 的次数 \(\le m - 1\)。

记 \(F(x)\) 的次数为 \(t\), \(F^R(x) = F(\frac{1}{x}) \cdot x^t\),直观理解就是把 \(F\) 的系数反转一下。

接下来将 \(\frac{1}{x}\) 带入到原式中,再两边同乘 \(x^n\),看看会发生什么

\[
x^nA(\frac{1}{x}) = x^{m}B(\frac{1}{x}) \cdot x^{n-m}D(\frac{1}{x}) + x^{n-m+1} \cdot x^{m-1}R(\frac{1}{x}) \\\\therefore A^R(x) = B^R(x)D^R(x) + x^{n-m+1}R^R(x)
\]

如果我们将新的等式放在模 \(x^{n-m+1}\) 的意义下,\(R(x)\) 就被消掉了!与此同时 \(D^R(x)\) 最高次为 \(x^{n-m}\),故不会受到任何影响,即模意义下求出的 \(D^R(x)\) 与非模意义下的完全相同。

\[
\begin{matrix}
A^R(x) \equiv B^R(x)D^R(x) & (\mod x^{n-m+1}) \\\\therefore D^R(x) \equiv A^R(x)[B^R(x)]^{-1} & (\mod x^{n-m+1})
\end{matrix}
\]

\(D(x)\) 就是 \(D^R(x)\) 反转一下系数,把 \(D(x)\) 代到最初的式子里就能得到 \(R(x)\) 了。

多项式多点求值

任务:给出多项式 \(F(x)\) 和集合 \(\{ x_1, x_2, \cdots , x_n \}\),要求返回一个集合 \(\{ F(x_1), F(x_2), \cdots , F(x_n) \}\)。

构造多项式 \(A(x) = (x - x_1)(x - x_2)\cdots(x - x_n)\),令 \(G(x) \equiv F(x) (\mod A(x))\),那么对于 \(\forall i \in [1, n], G(x_i) = F(x_i)\)。

为什么呢?因为

\[
F(x) = A(x)D(x) + G(x)
\]

而 \(\forall i \in [1, n], A(x_i) = 0, \therefore F(x_i) = G(x_i)\)。

于是就可以分治了。令 \(A_l(x) = \prod_{i = 1}^{\lfloor \frac{n}{2} \rfloor} { (x - x_i) }, A_r(x) = \prod_{i = \lfloor \frac{n}{2} \rfloor + 1}^n { (x - x_i) }\),然后把 \(F(x)\ \mod A_l(x)\) 和 \(F(x)\ \mod A_r(x)\) 扔下去递归了。

时间复杂度:\(O(n \log^2 n)\)

多项式 ln & exp

应该有不少人好奇这两个操作的组合意义是什么。事实上这个问题我并不是很清楚,我们也许可以通过泰勒展开后的系数研究一下它的组合意义,但它们的价值主要不在于组合意义,而在于其他方面的应用。

具体应用在讲完做法后立刻就会有一个,所以不必着急,我们先看看多项式取 ln 怎么求。

多项式取 ln

注意到这样一个等式

\[
(\ln f(x))‘ = \frac{f‘(x)}{f(x)}
\]

于是积分一下就可以轻易地求出 \(\ln f(x)\) 了

\[
\ln f(x) = \int \frac{f‘(x)}{f(x)}
\]

其中,求导和积分都能在 \(O(n)\) 时间内求出,所以总时间复杂度 \(O(n)\)。

细心的读者一定会问:积分之后,常数项如何确定?注意到这里的 \([x^0]f(x)\) 必须为 \(1\),也就是说 \([x^0] \ln f(x)\) 一定是 \(0\),那么积分完后常数项为 \(0\) 就好了。(如果 \([x^0]f(x) \ne 1\) 怎么求?QAQ 我也不会,因为我们似乎很难找到 \(\ln c (c > 0)\) 在剩余系下的表示……)

多项式求 exp

我们要求这样的一个函数 \(f(x)\),满足

\begin{matrix}

f(x) \equiv e^{g(x)} & (\mod x^n)

\end{matrix}

两边取对数,我们有

\begin{matrix}

\ln f(x) \equiv g(x) & (\mod x^n)

\end{matrix}

我们还是考虑倍增求 \(f\),套用上面泰勒展开推出来的结果,构造函数 \(H(f) = \ln f - g\),令 \(f_0 \equiv f (\mod x^{\lceil \frac{n}{2} \rceil})\),于是得到

\begin{matrix}

f \equiv f_0 - \frac{\ln f_0 - g}{f_0^{-1}} & (\mod x^n) \\

f \equiv f_0(1 - \ln f_0 + g) & (\mod x^n)

\end{matrix}

这样就 \(O(n \log n)\) 解决啦,你会发现求 exp 其实依赖于求 ln。同样地我们只能求 \([x^0]g(x) = 0\) 的 \(e^{g(x)}\)。

接下来就是一个经典的应用。

多项式乘方

求 \(f(x)\) 满足

\begin{matrix}

f(x) \equiv g(x) ^ k & (\mod x^n)

\end{matrix}

指数相关,考虑两边取对数

\begin{matrix}

\ln f(x) \equiv k \ln g(x) & (\mod x^n) \\

f(x) = e^{k \ln g(x)} & (\mod x^n)

\end{matrix}

这就解决了?别忘了常数项的问题,问题有两个:

  • \([x^0]g(x) > 1\),我们可以将常数项提出来,得到 \(g‘(x)\)(注意不是导数,这个就是 \(g(x)\) 的每项乘上常数项的逆元),那么问题变成了求 \((g‘(x))^k c^k\),\(c\) 代表常数项,就是在上面的过程进行完后每项乘上 \(c^k\) 即可;
  • \([x^0]g(x) = 0\),先位移到常数项不为 \(0\),即得到 \(g‘(x) = \frac{g(x)}{x^d}\),\(d\) 表示前缀连续 \(0\) 的个数,那么问题变成了求 \((g‘(x))^k x^{kd}\),就是上面的过程进行完后再位移 \(kd\) 位即可。

至此多项式乘方就完整地解决了。

原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8274115.html

时间: 2024-11-07 23:10:42

多项式操作汇总的相关文章

SQL字符串操作汇总

--将字符串中从某个字符开始截取一段字符,然后将另外一个字符串插入此处 select stuff('hello,world!',4,4,'****') --返回值hel****orld! --返回从指定位置开始指定长度的字符串 select substring('Hello,World!',2,10) --返回值ello,World --将字符串中某段字符替换为指定的字符串 select replace('hello,world!','ll','aa') --返回值heaao,world! --

提升效率的Linux终端快捷操作汇总

很多普通 Linux 桌面用户都对使用终端感到排斥和恐惧,其实它没大家想的那么复杂,很多常见操作都可以直接在终端中进行,如:安装软件.升级系统等. 无论你是新手还是 Linux 终端使用的老鸟,系统极客在此为大家总结了提升终端命令执行效率的快捷操作汇总,希望能帮助你学习和提升效率. 移动定位光标 在终端中移动光标和定位似乎非常不便,其实不是你想的那样,有很多种方式可以让键盘成为你的好朋友,只是需要掌握正确的方法而已. 定位单词 在长段的命令中,使用 Ctrl + ← 和 Ctrl + → 可快速

JS数组(Array)操作汇总

1.去掉重复的数组元素.2.获取一个数组中的重复项.3.求一个字符串的字节长度,一个英文字符占用一个字节,一个中文字符占用两个字节.4.判断一个字符串中出现次数最多的字符,统计这个次数.5.数组排序. 6.快排. 7.删除/添加数组项. 8.数组随机顺序输出. 9.数组求和.最大值. 10.判断是否为数组. 11.冒泡排序. 1.去掉重复的数组元素. Array.prototype.unique = function() { var ret = []; var o = {}; for(var i

Scala中List、Map、Set各类型操作汇总

1.Scala中List.Map.Set等各类型函数操作汇总 package com.scala.study import scala.collection.immutable.{Queue, TreeMap}import scala.collection.mutable /**  * Created by HP-PC on 2016/5/26.  */ object ScalaCaseDemo {  def main(args: Array[String]): Unit = {    prin

【转】C#路径/文件/目录/I/O常见操作汇总

文件操作是程序中非常基础和重要的内容,而路径.文件.目录以及I/O都是在进行文件操作时的常见主题,这里想把这些常见的问题作个总结,对于每个问题,尽量提供一些解决方案,即使没有你想要的答案,也希望能提供给你一点有益的思路,如果你有好的建议,恳请能够留言,使这些内容更加完善. 主要内容: 一.路径的相关操作, 如判断路径是否合法,路径类型,路径的特定部分,合并路径,系统文件夹路径等内容: 二.相关通用文件对话框,这些对话框可以帮助我们操作文件系统中的文件和目录: 三.文件.目录.驱动器的操作,如获取

Python学习总结3:元组、列表的操作汇总

参考博客:http://www.cnblogs.com/QG-whz/p/4782809.html 1. 是否可变 元组:用()或tuple函数定义,不可变(元素的值以及整个元组): 列表:用 [] 或list函数定义,可变. 2. 操作方法汇总 列表:Python序列操作全部通用,例如索引.分片.连接.乘法等.而且由于列表是可以修改的,所以多了一些元素赋值.元素删除.分片赋值的方法. 列表(对象)方法汇总: append:用于在列表末尾追加新的元素            count:用于统计某

链表实现多项式操作

最近看了weiss的数据结构,想记录一下自己的代码,这里是一个关于链表实现多项式的相关操作./**************************************@function:数据结构第三章,链表实现多项式的加减,乘也是类似,只不过还要合并,合并比较麻烦:*@data:2015/5/8*@Author:lss************************************/#include #include using namespace std;#define N1 2

JS 数组常见操作汇总,数组去重、降维、排序、多数组合并实现思路整理

壹 ? 引 JavaScript开发中数组加工极为常见,其次在面试中被问及的概率也特别高,一直想整理一篇关于数组常见操作的文章,本文也算了却心愿了. 说在前面,文中的实现并非最佳,实现虽然有很多种,但我觉得大家至少应该掌握一种,这样在面试能解决大部分数组问题.在了解实现思路后,日常开发中结合实际场景优化实现,提升性能也是后期该考虑的. 本文主要围绕数组去重.数组排序.数组降维.数组合并.数组过滤.数组求差集,并集,交集,数组是否包含某项等知识点展开,附带部分知识拓展,在看实现代码前也建议大家先自

Dom元素的属性常用操作汇总

2月20日汇总: 元素的属性操作:1.$(function(){ $(''#box).attr('class','box')}); 2.$('#box').attr({'title':'标题','class':'box'}) 添加多个通过定义对象添加:$("#box").attr("title",function(){ return '必须哦'}) 函数有返回值: 3.$("#box").removeAttr('title')删除指定元素属性: