多项式大学习
泰勒展开
感觉好有用啊,学一下
其实很简单。数学中,泰勒公式是一个用函数在某点的信息描述其附近取值的公式。如果函数足够平滑的话,在已知函数在某一点的各阶导数值的情况之下,泰勒公式可以用这些导数值做系数构建一个多项式来近似函数在这一点的邻域中的值。泰勒公式还给出了这个多项式和实际的函数值之间的偏差。
泰勒公式是将一个在 \(x=x_{0}\) 处具有 \(n\) 阶导数的函数 \(f(x)\) 利用关于 \((x-x_{0})\) 的 \(n\) 次多项式来逼近函数的方法。
泰勒展开式的通用形式:
\(f(x) = f(x_0) + f'(x_0)(x - x_0) + \frac{f''(x_0)}{2!}(x - x_0)^2 + \cdots + \frac{f^{(n)}(x_0)}{n!}(x - x_0)^n + R_n(x)\)
其中,\(R_n(x)\) 是余项。
麦克劳林展开式:
麦克劳林展开是泰勒展开在 \(x_0=0\) 的情况,形式为:
\(f(x) = f(0) + f'(0)x + \frac{f''(0)}{2!}x^2 + \cdots + \frac{f^{(n)}(0)}{n!}x^n + R_n(x)\)
作为OIER我当然是写求和符号了!
\(\sum\limits_{n=0}^{\infty} \frac{f^{(n)}(x_{0})}{n!}(x-x_{0})^{n}\)
\(\sum\limits_{n=0}^{\infty} \frac{f^{(n)}(0)}{n!}x^{n}\)
我觉得这个应该是要在光滑函数中才有用啊,因为如果你不光滑后面是不会影响到前面的
那么下一步是不是就是随便展开一点函数了
例题
-
\(sin(x)\)
\(\sum\limits_{n=0}^{\infty} \frac{sin(\frac{n\pi}{2})}{n!}x^{n}\)
-
\(e^{x}\)
\(\sum\limits_{n=0}^{\infty} \frac{1}{n!}x^{n}\)
深入理解
刷知乎,看到一道题要比较 \(\frac{\sqrt{5}+1}{2}\) 和 \(\ln 5\) 的大小
考虑对 \(\ln\) 进行泰勒展开,然后我们以 \(1\) 为中心泰勒展开,直接不行,因为我们根本不收敛。
即使我们求 \(\ln 2\),我们算了 \(20\) 项,误差仍然很大,在OI中,我们推式子也要注意一下。
生成函数
照着
https://www.luogu.com.cn/article/e8247hb2
https://www.cnblogs.com/reywmp/p/14783252.html
学的
智普清言还是很强的
-1. 符号及约定
\(x^0=1\) 对所有数成立,包括 \(0\)。
0. 前备知识
-
二项式系数 \({n \choose m}=\frac{n!}{m!(n-m)!}\)
-
\(n\in\mathbb R,m\in\mathbb N\) 时定义广义二项式系数 \(\binom n·m=\frac{n(n-1)\cdots(n-m+1)}{m!}\)。易知在 \(n\in\mathbb N\) 时广义二项式系数与之前的定理相符。
-
二项式定理 \((x+1)^{n}=\sum\limits_{i=0}^{n} {n \choose i} x^{i}\)
-
广义二项式定理:\((1+x)^n=\sum\limits_{i=0}^{\infty}\binom{n}{i}x^i\),其中 \(n\) 为任意实数。注意 \(n\in\mathbb N\) 时 \(i>n\) 的项都为 \(0\),这时即为一般的二项式定理。证明使用泰勒展开
生成函数简介
形式幂级数是数学中的一种形式级数,通常表示为:
\(F(x) = \sum\limits_{n=0}^{\infty} a_{n} f(x)\)
其中,\(a_n\) 是系数,\(x\) 是形式变量,不考虑 \(x\) 的取值范围和级数的收敛性。
其中 \(f(x)\) 称为核函数
(生成函数)形式幂级数的操作
形式幂级数的操作主要包括加法、乘法、求导等、积分,这些操作都是在形式上进行的,不考虑收敛性问题:
-
加法:
\(G(x) + H(x) = \sum\limits_{n=0}^{\infty} (a_n + b_n) x^n\)
-
乘法:
\(G(x) \cdot H(x) = \sum\limits_{n=0}^{\infty} \left( \sum\limits_{k=0}^{n} a_k b_{n-k} \right) x^n\)。也就是加法卷积
-
求导:
\(G'(x) = \sum\limits_{n=1}^{\infty} a_n n x^{n-1}\)
-
积分:
\(\int \left( \sum\limits_{n=0}^{\infty} a_n x^n \right) \mathrm{d}x = \sum\limits_{n=1}^{\infty} \frac{a_{n-1}}{n} x^n + C\)
-
移位
注意生成函数并不会出现负指数
\(x^m \sum\limits_{n=0}^{\infty} a_n x^n = \sum\limits_{n=m}^{\infty} a_{n-m} x^n\)
\(\frac{1}{x^m} \left( \sum\limits_{n=0}^{\infty} a_n x^n - \sum\limits_{n=0}^{m-1} a_n x^n \right) = \sum\limits_{n=0}^{\infty} a_{n+m} x^n\)
这个东西可以帮助我们研究序列的整体位移
AI说的好,我们利用生成函数只是研究序列的性质,不是研究具体值的,我们把 \(x\) 带进去其实也没有什么意义。
生成函数的类型
-
普通生成函数(OGF):核函数为 \(f_n(x) = x^n\)。普通生成函数是形式幂级数最常见的一种形式,它将序列 \(\{a_n\}\) 编码为生成函数 \(F(x) = \sum\limits_{n=0}^{\infty} a_n x^n\)。例如,序列 \(\{a_n\} = \{0, 1, 2, 3, \ldots\}\) 的普通生成函数是 \(\sum\limits_{n=0}^{\infty} n x^n\),而序列 \(\{a_n\} = \{0, 1, 2, 3\}\) 的普通生成函数是\(0 + x + 2x^2 + 3x^3\)。
-
指数生成函数(EGF):核函数为 \(f_n(x) = \frac{x^n}{n!}\)。指数生成函数在处理与排列和组合相关的问题时特别有用,因为它能够自然地处理涉及阶乘的情况。
-
狄利克雷生成函数:核函数为 \(f_n(x) = \frac{1}{n^x}\)。狄利克雷生成函数在数论中有着广泛的应用,特别是在研究数论函数和特殊数列时。
OGF
一些基础的OGF(普通生成函数的缩写)
-
当 \(x\in (0,1)\) 之间时 \(\frac{1}{1-x}=\sum\limits_{n=0}^{\infty} x^{n}\)
这个证明也很简单
设 \(S=\sum\limits_{n=0}^{\infty} x^{n}\)
\(xS=\sum\limits_{n=0}^{\infty} x^{n+1}\)
\(S(x-1)=x^{n+1}-1\)
\(S=\frac{1}{1-x}\)
-
拓展一下吧 \(\frac{a}{1-bx}=\sum\limits ?\)
尝试构造
\(S=\frac{a}{1-bx}\)
\(S(1-bx)=a\)
\(S-bx*S=a\)
\(S=\sum\limits_{n=0}^{\infty} a*(bx)^{n}\)
哈哈哈,做出来力!
-
\(\sum\limits_{n=0}^{\infty}[n=m] x^n=x^m\)
易证
-
\(\sum\limits_{n=m}^{\infty} x^n=\frac{x^m}{1-x}\)
\(S=\sum\limits_{n=m}^{\infty} x^n\)
\(xS=\sum\limits_{n=m+1}^{\infty} x^n\)
\(S-xS=x^{m}\)
\(S=\frac{x^{m}}{1-x}\)
-
\(\sum\limits_{n=0}^{\infty}{ {n+k-1}\choose{n}}x^n=\frac{1}{(1-x)^k}\)
不太会啊,展开一下吗
\(\frac{(n+k-1)!}{(k-1)!n!}x^n=\)
\(\frac{x^{n}}{(k-1)!} \prod\limits_{i=n+1}^{n+k-1}i\)
显然做不了
考虑如下递推关系 \({n+k-1 \choose n}={n+k-2 \choose n}+{n+k-2 \choose n-1}\)
使用数学归纳法,从上面推过来
设 \(G(x)=\sum\limits_{n=0}^{\infty}{ {n+k-1}\choose{n}}x^n\)
为了使用上面的组合数,我们这样
\(G(x)=\sum\limits_{n=0}^{\infty}{ {n+k-1}\choose{n}}x^{n}\)
展开的形式是有用的
\(G(x)=\sum\limits_{n=0}^{\infty} \frac{\prod\limits_{i=n+1}^{n+k-1}i}{(k-1)!} * x^{n}\)
\(G(x)=\sum\limits_{n=0}^{\infty} \frac{(n+k-1)*\prod\limits_{i=n+1}^{n+k-2}i}{(k-1)!} * x^{n}\)
换一种展开形式呢?因为我们不想让 \(n\) 这个东西到外面,因为 \(n\) 到外面是没有意义的
\(\frac{(n+k-1)!}{(k-1)!n!}x^n=\)
\(\frac{x^{n}}{n!} \prod\limits_{i=k}^{n+k-1}i\)
算了,第二种显然好转化
\(xG(x)={\sum\limits_{n=0}^{\infty}{ {n+k-1}\choose{n}} x^{n+1}}\)
确实,使用数学归纳法吧,因为第一个组合数里面并不能直接通过序列移位来改变答案。
但是这个数学归纳法只能求 \(k\) 为整数的情况,这个东西应该是 \(k\geq 1\) 的是吧。
您猜怎么着?
\(\frac{1}{(1-x)^{k}}=\sum\limits_{n=0}^{\infty}{ {n+k-1}\choose{n}}x^n\)
\((1-x)^{-k}=\sum\limits_{n=0}^{\infty} k(k+1)(k+2)\dots (k+n-1)(1-x)^{-k-n}\)
下面是第一步
\(f(x)=\sum\limits_{n=0}^{\infty} \frac{f^{(n)}(0)}{n!}x^{n}\)
\(f(x)=\sum\limits_{n=0}^{\infty} \frac{k(k+1)(k+2)\dots (k+n-1)(1-0)^{-k-n}}{n!}x^{n}\)
\(f(x)=\sum\limits_{n=0}^{\infty} \frac{k(k+1)(k+2)\dots (k+n-1)}{n!}x^{n}\)
直接泰勒展开就可以得到结果?其实不行吗?其实我们是在 0 处泰勒展开哦!然后就做完了
-
\(\sum\limits_{n=0}^{\infty}\frac{c^nx^n}{n!}=e^{cx}\)
泰勒展开
-
\(\sum\limits_{n=0}^{\infty}\frac{1}{n}x^{n}=\ln{\frac{1}{1-x}}\)
这东西泰勒展开太好用了啊,能不能把这题秒了。
\(f(x)=\sum\limits_{n=1}^{\infty} \frac{f^{(n)}(0)}{n!}x^{n}\)
对两边积分是不是就行了?
如果对两边求导,那么我们就可以把 \(\frac{1}{n}\) 搞掉,所以显然求导是更好的选择。
-
\(\sum\limits_{n>0}\frac{(-1)^{n-1}}{n}x^n=\ln(1+x)\)
斐波那契
求斐波那契的通项公式
哎哟,这题我会!
这个斐波那契数列有无穷项。
那么 \(a_{0}=0,a_{1}=1,a_{n}=a_{n-1}+a_{n-2}\)
我们规定
\(f(x)=\sum\limits_{n=0}^{\infty} a_{n}x^{n}\)
那么
\(xf(x)=\sum\limits_{n=1}^{\infty} a_{n-1}x^{n}\)
\(x^{2}f(x)=\sum\limits_{n=2}^{\infty} a_{n-2}x^{n}\)
\(f(x)-xf(x)-x^{2}f(x)=0+a_{1}x\)
\(f(x)=\frac{x}{1-x-x^{2}}\)
然后为了知道系数,我们希望把右边转化成 \(f(x)=\sum\limits a_{n}x^{n}\) 然后按照这个对应就行了
然后就要用到前面那个等比数列的公式了,我们把这个 \(\frac{x}{1-x-x^{2}}\) 拆成两个那样形式的相加,然后直接替换一下即可
设 \(\frac{a}{1-bx}+\frac{c}{1-dx}=\frac{x}{1-x-x^{2}}\)
\(\frac{a(1-dx)+c(1-bx)}{(1-bx)(1-dx)}=\frac{x}{1-x-x^{2}}\)
\(\frac{a-adx+c-bcx}{1-(b+d)x+bdx^{2}}=\frac{x}{1-x-x^{2}}\)
\(\begin{cases} a+c=0 \\ -ad-bc=1 \\ b+d=1 \\ bd=-1\end{cases}\)
来吧!可以先把 \(b,d\) 解出来然后就全部解出来
2简单数列的部分和
\(S(x)=\sum\limits_{n=0}^{\infty} s(n)x^{n}\)
\(S(x)=\sum\limits_{n=0}^{\infty} \sum\limits_{i=0}^{n} f(i) x^{n}\)
\(S(x)=\sum\limits_{i=0}^{\infty} f(i)\sum\limits_{i=n}^{\infty} x^{n}\)
对后面进行等比数列求和
\(S=\sum\limits_{i=n}^{\infty} x^{n}\)
\(xS=\sum\limits_{i=n+1}^{\infty} x^{n}\)
\(S-xS=x^{n}\)
\(S=\frac{x^{n}}{1-x}\)
\(S(x)=\sum\limits_{i=0}^{\infty} f(i)\frac{x^{n}}{1-x}\)
所以我们 \(s(x)\) 的生成函数只需乘一个 \((1-x)^{-1}\)
2 卡特兰数
然后发现这里不但有根号,还有正负号,这我直接不会了啊。看看题解。
考虑函数在 \(0\) 处的取值,那么 \(F(x)=1\),因为 \(0^0=1\),所以我们要取符号,这样 0 处就是 \(\frac{0}{0}=1\)。这里可能很奇怪,但就是这样子的?感觉也是比较有道理的
\(F(x)=\frac{1-\sqrt{1-4x}}{2x}\)
这个根号很不好搞,我们使用泰勒展开。哦,题解里面有个东西,叫做广义二项式定理。在前面写一下吧,和泰勒展开差不多。
P4451
整数拆分?多项式搞上!那么我们每个次项前面的系数就是斐波那契数了,那么我们直接乘起来,然后第 \(n\) 项的系数就是答案。
那么我们搞出这个多项式,\(\text{DFT}\) 点值乘 \(n\) 倍,然后再 \(\text{IDFT}\) 是不是就好了
发现 \(n\le 10^{10000}\),吓哭了。
假设我们斐波那契数列的生成函数是 \(F(x)\),那么答案就是 \(F(x)^{n}\)
考虑求这个生成函数的递推式,求递推式我们使用生成函数。那么我们生成函数的每个系数就是一个递推式。\(G(x)=\sum_{i=0}^{\infty} F(x)^{i}x^{i}=\frac{1}{1-F(x)}\)
我们又知道 \(F(x)=\frac{x}{1-x-x^{2}}\)
那么 \(G(x)=\frac{1-x-x^{2}}{1-2x-x^{2}}=1+\frac{x}{1-2x-x^{2}}\)
拆成生成函数的形式
注意到 \(G(x)\) 的第 \(0\) 项已经有一个系数,也就是那个 \(1\),我们只要把后面拆成生成函数的形式即可,最后再把这个加到 \(0\) 项的系数上即可
然后求个二次剩余就做完了。
和多项式综合
生成函数也是多项式,在利用生成函数推式子的时候我们会利用多项式科技去优化
CF958F3
这题不用组合数,所以我们使用OGF
我们给每种颜色搞一个 OGF,假设这个颜色有 \(b_{i}\) 个球
然后我们把所有 \(F(i)\) 乘起来,然后取 \(x^{k}\) 的系数即可
我们发现我们这样卷会T
利用线段树那种方法来合并,那么每个多项式只会被合并 \(\log\) 次,然后就做完了
据说答案用long long可以存下来?任意模数也可以过的。
拉格朗日插值:
直接有公式
\(f(k) = \sum\limits_{i=0}^{n} y_i \prod\limits_{j \neq i} \frac{k - x_j}{x_i - x_j}\)
代入 \(x_p\),发现原式 \(= y_p\)
对于 \(i\),如果 \(i = p\),那么后面那个求和号就是 \(\prod\limits_{j \neq p} \frac{x_p - x_j}{x_p - x_j}\),此时值为 1。如果 \(i \neq p\),那么 \(j\) 就有可能取到 \(p\),那么分子为 0,乘起来就是 0 了。这个实在太慢了。
重心拉格朗日插值法
把式子改一下:
设 \(g = \prod\limits_{i=0}^{n} (k - x_i)\)
\(f(k) = g \sum\limits_{i=0}^{n} \frac{y_i}{(k - x_i) \prod\limits_{j \neq i} (x_i - x_j)}\)
设 \(t_i = \prod\limits_{j \neq i} (x_i - x_j)\)
\(f(k) = g \sum\limits_{i=0}^{n} \frac{y_i}{(k - x_i) t_i}\)
这个可以 \(O(n)\) 询问,\(O(n^2)\) 预处理 \(t\) 和 \(g\)。
怎么想到这个东西的呢?因为我们发现分子只和 \(j\) 有关,所以提出来就可以了。哥们怎么式子推错了
在 \(x\) 连续的时候的做法(给出的点连续)
把 \(x_i\) 变成 \(i\):
\(f(k) = \sum\limits_{i=0}^{n} y_i \prod\limits_{j \neq i} \frac{k - j}{i - j}\)
考虑快速求后面这个东西:
分子可以表示为前缀积和后缀积:
\(pre_i = \prod\limits_{t=0}^{i-1} (k-t)\), \(suf_i = \prod\limits_{t=i+1}^{n} (k-t)\)
分母是阶乘形式:
\(\prod\limits_{j \neq i} (i-j) = i! \cdot (-1)^{n-i} \cdot (n-i)!\)
P4593
我们会使用 \(m+1\) 次亵渎,然后一次亵渎的贡献就是 \(\sum\limits_{i}^{m+1}\) 减去空位,空位这个简单,这个怎么求?
P5437
圆!
注意到每条边出现的概率是相等的,我们只要把这个概率求一下然后求边权和乘这个概率即可。分成两部分!每条边出现的概率是 \(\frac{n-1}{\frac{n(n-1)}{2}}\),因为我们一个树包含边的数量是 \(n-1\) 条,那么每条边的期望就是上面这个,因为每条边的贡献都是 \(1\),我们就好了。
对于后面,考虑递推。我们发现 \(f(n)=f(n-1)+\sum_{i=n+1}^{2n} i^{k}\)
考虑拉插,我们是否可以搞出前 \(k+2\) 个,这个是简单的,我们都可以线性处理。然后最后我们再插一下即可。
多项式科技
单位根
这个东西是虚数,虚数的乘法是模长相乘,辐角相加。
\((a+bi)(c+di)=ac-bd+(ad+bc)i\)
这个求出来也比较简单另外一种表示
\(z_{1}=r_{1}(cosA+i*sinA)\)
\(z_{2}=r_{2}(cosB+i*sinB)\)
相乘
\(z_{1}z_{2}=r_{1}r_{2}(cosAcosB-sinAsinB+i*(sinAcosB+cosAsinB))=r_{1}r_{2}(cos(A+B)+i*sin(A+B))\)
然后就做完了
除法同理,也就是模长相除,辐角相减
\(w_{n}^{k}\) 代表 \(cos(\frac{k * 2\pi}{n})+sin(\frac{k * 2\pi}{n})\)
然后一些东西可以类比三角函数,也比较直观
单位根相乘,模长都为 1,然后辐角就是上标,所以也可以直接相加
单位根的重要性质?
我之前怎么没有写,参考cyn的学习笔记
\(w_{n}^{k}=w_{A*n}^{A*k}\)
只要展开就好了吧
\(\text{FFT}\)
\(\text{DFT}\)
将多项式的系数表达变成点值表达。
这个函数就是传进去系数表达,然后变成点值表达。
\(F(x)=a_{0}+a_{1}*x+...+a_{n-1}*x^{n-1}\)
改下形式
\(F(x)=(a_{0}+a_{2}*x^2+a_{n-2}*x^{(n-2)})+x*(a_{1}+a_{3}*x^2+a_{n-1}*x^{n-2})\)
设 \(A_{1}(x)=a_{0}+a_{1}*x+..a_{n-2}*x^{\frac{n}{2}-1}\),\(A_{2}(x)=a_{1}+a_{3}*x+...a_{n-1}*x^{\frac{n}{2}-1}\)
所以 \(F(x)=A_{1}(x^2)+xA_{2}(x^2)\)
这个是个分治算法
从第一层开始
设 \(k\le \frac{n}{2}\) 对于现在处理的前半部分
\(F(w_{n}^{k})=A_{1}(w_{n}^{2k})+w_{n}^{k}A_{2}(w_{n}^{2k})\)
\(=A_{1}(w_{\frac{n}{2}}^{k})+w_{n}^{k}A_{2}(w_{\frac{n}{2}}^{k})\)
后半部分
\(F(w_{n}^{k+\frac{n}{2}})=A_{1}(w_{n}^{2k+n})+w_{n}^{k+\frac{n}{2}}A_{2}(w_{n}^{2k+n})\)
\(=A_{1}(w_{n}^{2k})-w_{n}^{k}A_{2}(w_{n}^{2k})\)
\(=A_{1}(w_{\frac{n}{2}}^{k})-w_{n}^{k}A_{2}(w_{\frac{n}{2}}^{k})\)
可以理解吧。所以我们现在就是要求 \(A_{1}\) 和 \(A_{2}\) 在 \(k\le \frac{n}{2}\) 下面的这 \(k\le \frac{n}{2}\) 个值。
我们把系数传下去,然后求出 \(A_{1}\),这个传下去的是一个 \(\frac{n}{2}\) 的多项式,然后取的就是前面一点。然后后面多项式再乘一下
然后我就会 \(\text{FFT}\) 了
就差不多是第一层,我们的下标是 \(w_{n}^{0}\),所以是 1,所以最后一层直接就是系数了。
然后考虑拓展,这个就简单了,就是点值
合并的话就是所有都合并是吧
就是最后一次你是一个系数乘,然后就是一个一个合并,然后就差不多了
这就相当于每一个坐标,计算的系数每次都会多一倍
I \(\text{DFT}\)
设 \(g(k)=\sum\limits_{i=0}^{n-1} (w_{n}^{k})^{i}f(i)\)
\(f\) 为系数,\(g\) 为多项式取 \(x\) 的值
我们 \(\text{DFT}\) 算完的点值序列是 \(p\),那么系数就是将负的带进去,然后除以 \(n\)。考虑证明这个东西
\(n*f_{k}=\sum\limits_{i=0}^{n-1} (w_{n}^{-k})^{i} p[i]\)。也就是这个点的点值表示的纵坐标。
\(\sum\limits_{i=0}^{n-1} (w_{n}^{-k})^{i} p[i]\)
\(\sum\limits_{i=0}^{n-1} (w_{n}^{-k})^{i} \sum\limits_{j=0}^{n-1} f_{j}*(w_{n}^{i})^{j}\)
\(\sum\limits_{i=0}^{n-1}\sum\limits_{j=0}^{n-1} w_{n}^{-ki+ij}\)
\(\sum\limits_{i=0}^{n-1}\sum\limits_{j=0}^{n-1} w_{n}^{i*(j-k)}*f_{j}\)
若 \(j=k\),那么贡献就是 \(n*f_{k}\),因为指数为\(0\)
如果 \(j!=k\),那么贡献就是 \(\sum\limits_{i=0}^{n-1}(w_{n}^{i(j-k)}) f(j)\)
\(w_{n}^{j-k}\sum\limits_{i=0}^{n} (w_{n}^{i}) f(j)\)
发现 \(f(j)\) 固定,所以提到外面。然后这个 \(\sum\limits\),我们发现 \(w\) 的值比较对称所以值为 \(0\)。然后就做完了。
关于实现
\(f\) 里面存的是系数,然后算好反回
常数优化:
1.一些细节上优化(开个变量临时存一下什么的)
2.一些数组赋值很花时间
如果我们知道最后每个下标搞的是那个那么就可以节省很多时间
3.只做两次 \(\text{FFT}\) ,构造多项式。
关于中间单位根的事情,其实没怎么听懂,下面详细讲解一下
其实我们递归到底层是求 \(w^{0}_{1}\) 的点值表达,所以我们就根本不用干任何事情,然后按照那个一层一层递归上去,嗯,还好理解
在 cyn 的题解中 \(F\) 为多项式的系数表达,\(G\) 为多项式的点值表达,说的很清楚
to数组
也就是找到我们变换之后下标的那个数组。
通过观察我们可以得到结论,我们变换之后的下标是原下标的二进制反转。为什么会这样呢?如果是偶数,那么我们会把它提到前面,那么这些原本最低位是 \(0\) 的数最高位就是 \(0\) 了。然后其他同理
所以我们使用二进制随便刻画这个东西即可
FMT/FWT
放到这里吧,很巧妙使用了 \(\text{FFT}\) 的性质。主要的思想就是利用了从小到大把下标的二进制位搞清楚,这样就很方便分讨
或卷积
\(\text{DFT}\)
我们要求的是这么一个数组 \(c[i]=\sum\limits_{j|i=i} a[j]\)
这难道不是子集和吗?对的对的,但是我们这里实现不一样。都可以啦
我们对于一个数组,想要求其的 \(c\) 数组,所以我们直接奇偶分组,然后内部的贡献已经算完了,然后因为是子集和,这个长度又是二进制,所以我们可以得到 \(f[j]=f[j],f[j+k]=f[j+k]+f[j]\)
然后就得到了和高维前缀和一样的式子
\(\text{IDFT}\)
那么我们反过来,我们对于一个数组要将其中间的贡献消掉,那么我们把加号改成减号即可。
然后我们就是对两个式子分别 \(\text{DFT}\),然后中间显然是可以乘起来的,然后就可以了
与卷积
仿照上面,\(f[j]=f[j]+f[j+k]\) 和 \(f[j]=f[j]-f[j+k]\)
异或卷积
仿照上面,我们还是构造 \(c[i]=\sum\limits_{j\oplus i=i} a[j]\),但是我们发现一点用都没有,因为上面那种是类似 \(max,min\) 的运算,这里则和加法差不多。
这里很特殊,我们定义运算 \(x\oplus^{'}y=\text{popcount}(x\& y)\mod 2\),然后定义 \(\sum\limits_{i\oplus^{'}j=0}a_{j}-\sum\limits_{i\oplus^{'}j=1}a_{j}\)
\(a·b=(\sum\limits_{i\oplus^{'}j=0}a_{j}-\sum\limits_{i\oplus^{'}j=1}a_{j})(\sum\limits_{i\oplus^{'}j=0}b_{j}-\sum\limits_{i\oplus^{'}j=1}b_{j})\)
\(\sum\limits_{i\oplus^{'}j=0}a_{j}\sum\limits_{i\oplus^{'}k=0}a_{k}\)
考虑把下标变成一个二元组
\(\sum\limits_{i\oplus^{'}j=0,i\oplus^{'}k=0}a_{j}a_{k}-\sum\limits_{i\oplus^{'}j=0,i\oplus^{'}k=1}a_{j}a_{k}-\sum\limits_{i\oplus^{'}j=1,i\oplus^{'}k=0}a_{j}a_{k}+\sum\limits_{i\oplus^{'}j=1,i\oplus^{'}k=1}a_{j}a_{k}\)
\(\sum\limits_{(i\oplus^{'}j)\oplus (i\oplus^{'}k)=0}a_{j}a_{k}-\sum\limits_{(i\oplus^{'}j)\oplus (i\oplus^{'}k)=1}a_{j}a_{k}\)
然后对下面进行分析
\((pc(i\& j) \mod 2)\oplus (pc(i\& k) \mod 2)=pc(i\& j)\oplus pc(i\& k) \mod 2\)
考虑证明 \(pc(i\& j)\oplus pc(i\& k) \mod 2=pc(i\& (j\oplus k)) \mod 2\)
对于 \(j\) 和 \(k\) 相同的那位,和 \(i\) 进行 \(\&\) 之后贡献肯定相同,所以对于两边的奇偶性产生的贡献也相同,所以就证明完了。然后上面的式子就自然是 \(\text{DFT}(c)\) 了。
那么具体怎么操作呢?对于前面,我们的最低位已经确定,我们加上最高位之后发生了什么变化呢?无论如何这个 \(j\) 是什么,我们的 \(\text{popcount}\) 都不会改变,那么我们直接加上后面的所有答案即可。对于后面,我们加上最高位之后,前面还是不会改变的,对于后面,前面的贡献还是一样的,后面的会变成相反数,然后就做完了。
还原还是倒过来
子集卷积
其中每个限制都是好做的,所以我们考虑拆成两维的一个东西,第一维是值,第二维是 \(\text{popcount}\),这里约定 \(|i|=\text{popcount}(i)\)
\(c_{k,|k|}=\sum_{i|j=k,|i|+|j|=|k|} a_{i}b_{j}\)
我们这样做,我们对于每个 \(|k|\) 都做 \(|k|\) 遍,也就是枚举 \(a\) 和 \(b\) 的大小。这样的话我们要对于所有的 \(a,b\) 都做一遍 $ \(\text{DFT}\) $,然后 \(n^{2}\) 个互相乘,然后最后 $ \(\text{DFT}\) $ 即可。
应用
可以做下标和为常数的卷积和、差为 \(1\sim i\) 的卷积。
多项式乘法
我们的多项式乘法每个系数
我们把它转成点值表示的时候,因为处理完之后还是多项式。我们每取一个点,那么最终的多项式乘起来就是对应点值的乘积。所以我们就做完了。
那么我们 \((a_{0}+a_{1}*x+a_{2}*x^{2}+a_{3}*x^{3}+....+())()\)
我们的 i \(\text{DFT}\) 直接搞就好了。
所以对于这种加法卷积的东西。我们可以利用分配律转化成多项式。有多少个加法就有多少 +1 个 \(\text{FFT}\)
P3338
\(\sum\limits_{i=1}^{j-1} q_{i}*f_{j-i}\)
\(\sum\limits_{i=1}^{j-1} q_{n-i+1}*f_{i-j}\)
第一个好求
第二个我们将一个数组反转,比如说 \(q\)
那么我们的式子就变成,应该是对的嗷
\((q_{1}+q_{2}+...+q_{n})(f_{1}+f_{2}+...+f_{n})\)
哦,代表的就是这个系数,懂了,这个就是用那个拆 ()() 很快的方式,就是直接算系数,所以每个系数就代表这么一个东西,然后这个系数可以由点值转化而来,然后就一步一步往前推是不是就可以了。
考虑分两步做,
式子变成
\(\sum\limits_{i=0}^{j} q[i] * f[j-i]-\sum\limits_{i=j}^{n} q[i]*f[i-j];\)
P3723
已经知道这个是多项式题了
拆一下式子 \(\sum\limits x_{i}^{2}+y_{i}^{2}-2x_{i}y_{i}\)
前面容易,直接加上去,然后我们此时就是要求。
\(\sum\limits (x_{i}+k-y_{i})^{2}\)
\(\sum\limits x_{i}^{2}+y_{i}^{2}+k^{2}+2x_{i}k-2x_{i}y_{i}-2ky_{i}\)
\(\sum\limits x_{i}^{2}+y_{i}^{2}+k^{2}+2k*{x_{i}-y_{i}}-2x_{i}y_{i}\)
发现除了最后一项都和前面无关,就是要最大化最后一位的值。
我们要求最后的最大值,那么我们考虑枚举移了多少位
这里直接 \(\sum\limits\) 了
\(\sum\limits_{i=0}^{n-1-j} x_{i+j}y_{i}+\sum\limits_{i=n-j}^{n-1} x_{i+j-n}y_{i}\)
我们将 \(y\) 翻转
\(\sum\limits_{i=0}^{n-1-j} x_{i+j}y_{n-i-1}+\sum\limits_{i=n-j}^{n-1} x_{i+j-n}y_{n-i-1}\)
\(\sum\limits_{i=0}^{n-1-j} x_{i+j}y_{n-i-1}+\sum\limits_{i=0}^{j-1} x_{i}y_{j-i-1}\)
P4721 分治 \(\text{FFT}\)
考虑 cdq 分治,假如已经处理出左半边了,考虑对右半边的贡献。假设我们已经处理好了左半边,那么对右边每一个数的贡献就是 \(\sum\limits_{i=0}^{mid-1} f_{i}g_{j-i}\),然后右边就加上这些数即可。时间复杂度 \(O(n \log^2 n)\)
讲得很简略啊,假如我们已经知道了 \(f[l,mid]\),那么我们把这个和 \(g[1,mid]\) 做多项式乘法,然后就做完了。\(log\) 层,每层 \(n\log n\)
P4173
考虑设计一个比较函数,这个简单,我们将 \(a\) 字符和 \(b\) 字符相等变为 \(x_{i}y_{i}(x_{i}-y_{i})\),往后 \(m\) 位都匹配上那么直接加起来为0即可,所以这个函数变成 \(x_{i}y_{i}*(x_{i}-y_{i})^{2}\),
\(\sum\limits_{i=0}^{n-m} \sum\limits_{j=i}^{j+m-1} x_{j-i}y_{j}(x_{j-i}-y_{j})^{2}\) 然后就有点多项式的样子了,拆式子。
设 \(x_{j-i}y_{j}=A\)
\(A*(x_{j-i}^{2}+y_{i}^{2}-2A)\)
发现 \(x_{j-i}\) 的和是定值,然后 \(y_{i}^{2}\) 是前缀和
\(A * K+A * (sum[i+m-1]-sum[i-1]) -2*A^{2}\)
哈哈,这是我们惊奇发现除了 \(A\),其他都可以 \(O(1)\) 求出。求这个\(A\) 就简单了
考虑求 \(A\)
\(\sum\limits_{j=i}^{j+m-1} x_{j-i}y_{j}\)
\(\sum\limits_{j=0}^{m-1} x_{j}y_{j+i}\)
考虑翻转这个 \(x\)
\(\sum\limits_{j=0}^{m-1} x_{m-j-1}y_{j+i}\)
然后就做完了,然后发现假了,为什么呢,应为有些没对应
\(\sum\limits_{j=0}^{m-1} x_{j}y_{j+i}(x_{j}-y_{j+i})^{2}\)
还是翻转
\(\sum\limits_{j=0}^{m-1} x_{m-j-1}y_{j+i}(x_{m-j-1}-y_{j+i})^{2}\)
我们貌似要做 9 遍 ntt
\(x_{m-j-1}^{3}y_{j+i}+x_{m-j-1}y_{j+i}^{3}-2*+x_{m-j-1}^{2}y_{j+i}^{2}\)
然后搞一下即可
全家桶
思路基本照抄szr的全家桶,也就是更像人写的东西
class poly{
protected:
typedef const int ci;
static ci mod=998244353,g=3,invg=(mod+1)/3,S=1<<21;
int qpow(int x,int p){
int ans=1;
while(p){
if(p&1) ans=1ll*ans*x%mod;
x=1ll*x*x%mod;
p>>=1;
}
return ans;
}
public:
vector<int>a;
poly(){ a={0}; }
poly(ci &x){ a={x}; }
poly(const vector<int>&v){ a=v; }
poly(const poly &b){ a=b.a; }
void resiz(ci &n){ a.resize(n,0); }
poly siz(ci &n){ poly f(*this);f.resiz(n);return f; }
poly rev(){ poly b(*this); reverse(b.a.begin(),b.a.end());return b; }
int &operator [](ci &i){ return a[i]; }
int size(){ return a.size(); }
void print(){ for4(i,a) cout<<i<<" ";cout<<"\n";}
void ntt(ci &lim,int *f,const bool &op,int *to){
for2(i,0,lim) if(i<to[i]) swap(f[i],f[to[i]]);
for(int len=2;len<=lim;len<<=1){
ci k=len>>1;
int w;
if(!op) w=qpow(g,(mod-1)/len);
else w=qpow(invg,(mod-1)/len);
for(int i=0;i<lim;i+=len){
int wn=1;
for2(j,i,i+k){
ci p=1ll*f[j|k]*wn%mod;
f[j|k]=f[j]-p;
if(f[j|k]<0) f[j|k]+=mod;
f[j]+=p;
if(f[j]>=mod) f[j]-=mod;
wn=1ll*wn*w%mod;
}
}
}
if(op){
ci inv=qpow(lim,mod-2);
for2(i,0,lim) f[i]=1ll*f[i]*inv%mod;
}
}
poly operator*(poly &b){
poly c;
ci n=a.size(),m=b.size();
int lim=n+m-1;
ci k=lim;
int lg=0;
while((1<<lg)<lim) lg++;
lim=1<<lg;
static int to[S];to[0]=0;
for2(i,1,lim) to[i]=to[i>>1]>>1|(i&1)<<(lg-1);
static int f[S],g[S];
for2(i,0,lim){
if(i<n) f[i]=a[i];
else f[i]=0;
if(i<m) g[i]=b[i];
else g[i]=0;
}
ntt(lim,f,0,to),ntt(lim,g,0,to);
for2(i,0,lim) f[i]=1ll*f[i]*g[i]%mod;
ntt(lim,f,1,to);
c.resiz(k);
for2(i,0,k) c[i]=f[i];
return c;
}poly operator*(poly &&b){ return (*this)*b; }
poly operator+(poly &b){
poly c;
ci n=min((int)a.size(),b.size()),m=max((int)a.size(),b.size());
c.resiz(m);
for2(i,0,n){
c[i]=a[i]+b[i];
if(c[i]>=mod) c[i]-=mod;
}
if(m==a.size()) for2(i,n,m) c[i]=a[i];
else for2(i,n,m) c[i]=b[i];
return c;
}poly operator+(poly &&b){ return (*this)+b; }
poly operator-(poly &b){ return (*this)+b.neg(); }
poly operator-(poly &&b){ return (*this)+b.neg(); }
poly operator/(poly &b){
ci n=a.size(),m=b.size();
return (((*this).rev())*(b.rev().inv(n-m+1))).siz(n-m+1).rev();
}poly operator/(poly &&b){ return (*this)/b; }
poly operator%(poly &b){ return (*this)-(*this)/b*b; }
poly operator%(poly &&b){ return (*this)%b; }
poly &operator=(poly &b){ a=b.a;return *this; }
poly &operator=(poly &&b){ a=b.a;return *this; }
poly neg(){ poly c(*this);ci len=c.size();for2(i,0,len) c[i]=(!c[i] ? 0 : mod-c[i]);return c; }
poly dao(){
poly c(*this);
if(c.size()==1) return {0};
for2(i,0,c.size()-1)
c[i]=1ll*c[i+1]*(i+1)%mod;
c.resiz(c.size()-1);
return c;
}
poly ji(int b){
poly c(*this);
c.a.puba(0);
ci len=c.size();
vector<int>inv;
inv.resize(c.size());
inv[0]=1;
for2(i,1,len) inv[i]=1ll*inv[i-1]*i%mod;
int tot=qpow(inv[len-1],mod-2),suf=1;
for3(i,c.size()-1,1){
c[i]=1ll*c[i-1]*tot%mod*inv[i-1]%mod*suf%mod;
suf=1ll*suf*i%mod;
}
c[0]=b;
return c;
}
poly inv(int n){
poly ans(qpow(a[0],mod-2));
int lim=2;
while(lim<(n<<1)){
poly b=this->siz((a.size()<=lim)?(a.size()):lim);
ans=ans+ans-b*ans*ans;
ans.resiz(lim);
lim<<=1;
}ans.resiz(n);
return ans;
}
poly ln(int n){
return ((((*this).dao())*((*this).inv(n))).ji(0)).siz(n);
}
poly exp(int n){
poly ans(1);
int lim=2;
while(lim<(n<<1)){
poly b=this->siz((a.size()<=lim)?(a.size()):lim);
ans=ans-ans*(ans.ln(lim)-b);
ans.resiz(lim);
lim<<=1;
}ans.resiz(n);
return ans;
}
poly sqrt(int n){
poly ans(1);
int lim(2);
while(lim<(n<<1)){
poly b=this->siz((a.size()<=lim)?(a.size()):lim);
ans=((ans*ans+b)*((ans+ans).inv(lim))).siz(lim);
lim<<=1;
}
return ans.siz(n);
}
poly qpow(int k){
int n=(*this).size();
poly b(k);
return (((*this).ln(n))*b).exp(n).siz(n);
}
};
多项式求逆
多项式乘法逆
多项式求逆?有点东西
就是让你求一个多项式的逆,使得对于每一个 \(x\),两个多项式的值互为倒数。容易发现如果有解的话,项数应该是无限的。我们的算法可以花 \(n\log n\) 的时间求出前 \(n\) 项,还是很智能的。
假如我们已经求出了前 \(\frac{n}{2}\) 项,考虑怎么推到后面
已知 \(A\)
设 \(A*B \equiv 1 \pmod {x^{\frac{n}{2}}},A*B'\equiv 1 \pmod {x^n}\)
则 \(A*B'\equiv 1 \pmod {x^{\frac{n}{2}}}\)
因为 \(A\) 的每一个点值不可能在模 \(x^{\frac{n}{2}}\) 意义下都是 0,所以只能是 \(B\) 了
\(B-B'\equiv 0 \pmod {x^{\frac{n}{2}}}\)
\(B^2-2BB'+B'^2\equiv 0 \pmod {x^n}\)
总得有 \(A\) 吧,左右同乘 \(A\)。我们要求 \(B\),那么总得有单独一个 \(B\) 吧
\(B-2B'+AB'^2\equiv 0 \pmod {x^n}\)
\(B\equiv2B'-AB'^2 \pmod {x^n}\)
多项式ln
设 \(G(x)=ln(F(x))\)
求导 \(G(x)^{'}=F(x)^{'}*\frac{1}{F(x)}\)
求导显然是简单的,然后我们对 \(F(x)\) 多项式求逆,然后和 \(F(x)^{'}\) \(\text{FFT}\) 一下。然后再把 \(G(x)\) 还原。常数项是什么呢?那么就是 \(0\) 了,因为我们保证了 \(F(x)\) 常数项为 \(1\)
多项式牛顿迭代
并没有例题
多项式很光滑啊,所以可以牛顿迭代和泰勒展开
对于函数 \(F(G(x))\equiv 0\pmod {x^{n}}\) 我们要求其的零点。同样的我们这里用中文说就是我们每取一个 \(x\) ,我们都会使得 \(G(x)\) 取到 \(F\) 的零点,然后这样就构成了一个函数关系,然后我们取其的前 \(n\) 项。
考虑倍增
首先我们要求出 \(n=1\) 的情况,也就是确定 \(G\) 的一个常数项并且其作为 \(F\) 的一个零点
假设我们已经求出了 \(F(G(x_{0}))\equiv 0\pmod {x^{\frac{n}{2}}}\),考虑将 \(F(G(x))\) 在 \(G(x_{0})\) 处泰勒展开。
注意我们这里泰勒展开是在一个多项式里面展开的,所以我们这个多项式就相当于 \(x\),所以我们对 \(F(G(x_{0}))\) 求导其实并不是复合函数求导
\(F(G(x))=F(G(x_{0}))+\frac{F^{'}(G(x_{0}))}{1!}(G(x)-G(x_{0}))+\frac{F(G(x_{0}))^{'(2)}}{2!}(G(x)-G(x_{0}))^{2}\dots\)
注意到我们的 \(G(x)\) 和 \(G(x_{0})\) 后 \(\frac{n}{2}\) 项是相同的,那么我们 \((G(x)-G(x_{0}))^{p} \equiv 0\pmod{x^{n}}(p\ge 2)\)
所以可以化简为 \(F(G(x))=F(G(x_{0}))+F^{'}(G(x_{0}))(G(x)-G(x_{0}))\)
因为 \(F(G(x))\equiv 0\pmod{x^{n}}\)
那么 \(F(G(x_{0}))+F^{'}(G(x_{0}))G(x)-F^{'}(G(x_{0}))G(x_{0})\equiv 0 \pmod{x^{n}}\)
\(F(G(x_{0}))+F^{'}(G(x_{0}))G(x)\equiv F^{'}(G(x_{0}))G(x_{0})\pmod{x^{n}}\)
\(G(x)\equiv G(x_{0})-\frac{F(G(x_{0}))}{F^{'}(G(x_{0}))} \pmod{x^{n}}\)
多项式exp
我们要计算 \(B(x)=e^{A(x)}\)
两边取对数,移项 \(\ln B(x)-A(x)=0\)
设 \(F(B(x))=\ln B(x)-A(x)\)
那么我们现在一个 \(x\) 的值会对应 \(B(x)\),那么一个 \(B(x)\) 会对应一个 \(\ln B(x)\) 和 \(A(x)\),那么我们自然就可以构造这个复合函数了
所以我们现在就是要求这个东西的零点,使用牛顿迭代
\(B(x)\equiv B(x_{0})-\frac{F(B(x_{0}))}{F(B(x_{0}))^{'}} \pmod{x^{n}}\)
\(B(x)\equiv B(x_{0})-\frac{\ln B(x_{0})-A(x)}{\frac{1}{B(x_{0})}} \pmod{x^{n}}\)
导完为什么是 \(\frac{1}{B(x_{0})}\) 详见牛顿迭代的注释那里
\(B(x)\equiv B(x_{0})-B(x_{0})(\ln B(x_{0})-A(x)) \pmod{x^{n}}\)
所以我们直接贴板子即可
多项式带余除法
\(F(x) = Q(x) * G(x) + R(x)\)
先让我自己发明一下,这题用什么手法呢?倍增不行,我们考虑正常数学手法。我们考虑大除法那种东西,我们找到最高的系数,然后除一除,然后再减一减,这样我们一共要做 \(n-m\) 遍 \(\text{FFT}\) ,真的太蠢了。我们这边的 \(\text{FFT}\) 是一个只有一项的一个多项式,考虑在这个方面做文章。
不会,看题解。啊?
大体的思路就是先把这个余数去了,然后用多项式求逆,然后在减出余数
设 \(A_{R}(x)=x^nA(\frac{1}{x})\)
写出来 \(A_{R}(x)=x^n\sum\limits_{i=0}^{n} a_{i}\frac{1}{x^{i}}\)
\(\sum\limits_{i=0}^{n}a_{i}x^{n-i}\)
我们发现这是一个翻转系数的操作
那么我们在 \(\pmod {x^{n-m+1}}\) 意义下进行操作
然后我们直接减一下就可以得到 \(R\)
多项式开方
先自己发明一下。二次剩余是怎么做的?
二次剩余要随机一个不是二次剩余的东西,显然我们总不能随机一个多项式吧
所以我们还是考虑倍增,假设我们已经求出 \(B^{'^{2}}(x) \equiv A(x) \pmod {x^{\frac{n}{2}}}\)
我们设 \(B^{2}(x)\equiv A(x) \pmod{x^{n}}\)
但是我们发现靠上面的式子是无法消去 \(B^{2}\) 的指数的。看题解,上面分析有道理的
\(B^{'}(x)\equiv B(x)\pmod {x^{\frac{n}{2}}}\)
\(B^{'}(x)-B(x)\equiv 0\pmod {x^{\frac{n}{2}}}\)
\(B^{'2}(x)-2B^{'}(x)B(x)+B^{2}(x)\equiv 0 \pmod {x^n}\)
\(B^{'2}(x)-2B^{'}(x)B(x)+A(x)\equiv 0 \pmod {x^n}\)
\(B(x)\equiv \frac{B^{'2}(x)+A(x)}{2B^{'}(x)} \pmod {x^n}\)
多项式快速幂
看到觉得很简单?注意到指数非常大,考虑取对数,但是这个底数是多项式啊。
考虑取对数 \(\ln B(x)\equiv \ln A^{k}(x)\)
所以我们现在搞出右边的式子之后就是多项式 \(exp\)。然后我们对左边 \(k\) 输入的时候直接取模,然后就做完了。
对于加强版,我们 \(a_{0}\) 不是 \(1\) 了,那怎么办呢?我们分两类讨论
- \(a_{0}=0\),那么我们找到最小的不是 \(0\) 的那位,然后往下搞,然后找到末尾 \(0\) 的个数,然后最后再升回去即可
- 否则我们对所有系数都提出 \(a_{0}\),式子变为 \(a_{0}^{k}(\sum\limits_{i=0}^{n} \frac{a_{i}}{a_{0}} x^{i})^{k}\)
注意我们这里的 \(a_{0}\) 的指数是要对 \(mod-1\) 取模的(费马小定理)。
下降幂多项式乘法
下降幂就是 \(x^{\underline n}=\frac{x!}{(x-n)!}\)
\(F(x)=a_{0}+a_{1}x+a_{2}x(x-1)+a_{3}x(x-1)(x-2)+\dots+a_{n}x(x-1)(x-2)\dots(x-n+1)\)
我们显然很难得出正常的多项式,考虑有什么特殊规律
MTT
取三个模数,分别算出答案,然后将答案用 CRT 合并
一个很 naive 的想法是 \(3*3\) 次 NTT。过不了。
FFT可以摆脱模数的限制,我们直接乘起来,发现精度甚是感人。
设 \(c=(int)\sqrt{p}\),将系数拆成 \(A[i]=A1[i]*c+A2[i]\),\(B\) 数组同理
那么 \(A*B=A1*B1*c^{2}+(A1*B2+A2*B1)*c+A2*B2\)
把这四个搞出来就行了!
天真的想法是做 \(4\) 遍 \(DFT\),然后对四个做点乘,然后 \(4\) 遍 \(IDFT\)
还是不够快。
我们先搞出 \(P,Q\) 的点值表达,然后观察一下 \(P^{'}\) 的点值表达和 \(P\) 有什么联系。我们发现,既然变成了共轭,那么只要单位根也是共轭,那么结果就是虚部取反。那么单位根变成了共轭就是将数组反过来4
然后 \(2\) 个实部虚部分别对应 \(4\) 个多项式,然后就做完了。
MTT乘法逆
板子吧
更多的科技
高维 \(\text{FFT}\)
先从二维的情况开始
定义 \(F(x,y)=\sum\limits_{i<n,j<m} a_{i,j}x^{i}y^{j}\)
那么我们同样,我们要求这个的点值表示,那么我们就带入 \(n*m\) 个单位根,然后我们就得到了一个点值表示的多项式,然后我们再 $I \(\text{DFT}\) $ 一下。
换一种角度解释
\(F(x,y)*G(x,y)=\sum\limits_{i_{1}<n}\sum\limits_{j_{1}<n} a_{i_{1},j_{1}}x^{i_{1}}y^{j_{1}}\sum\limits_{i_{2}<m}\sum\limits_{j_{2}<m} b_{i_{2},j_{2}}x^{i_{2}}y^{j_{2}}\)
交换顺序
\(=\sum\limits_{i_{1}<n}\sum\limits_{i_{2}<m}x^{i_{1}+i_{2}}\sum\limits_{j_{1}<n}\sum\limits_{j_{2}<m}y^{j_{1}+j_{2}} a_{i_{1},j_{1}}b_{i_{2},j_{2}}\)
变一下
\(=\sum\limits_{i_{1}+i_{2}=k_{1}}x^{k_{1}}\sum\limits_{j_{1}+j_{2}=k_{2}}y^{k_{2}} a_{i_{1},j_{1}}b_{i_{2},j_{2}}\)
考虑把 \(y\) 变成点值表示,也就是我们把后面的 \(j_{1},j_{2}\) 看作变量,然后作为系数做 \(\text{DFT}\) ,然后我们就得到了两个个多项式,然后乘以下(不用 \(\text{IDFT}\) )。其点值表示就是 \((w_{0},?_{0})\dots\)
然后我们继续整理。
\(=\sum\limits_{i_{1}+i_{2}=k_{1}} x^{i_{1}}x^{i_{2}}*不知名多项式点值表示\)
这里的系数差不多是点值表达得到的东西了
我们发现这又是一个 \(\text{FFT}\)。所以我们继续带入点值表示,就得到了和上面一样的结果。用人话说这个结果就是我们每行做 \(\text{DFT}\),然后得到点值表示,然后每列做 \(\text{DFT}\),然后对应点相乘,然后再 \(\text{IDFT}\) 回去。感性理解,还是比较好理解的。
循环卷积
以一道例题引入
这里值域应该是 \(<2^{20}\),这样做起来简单一点
我们发现我们 \(a\le 2^{20}\) 那么我们发现二进制只有前 \(20\) 位,这样我们只要求 \(s\mod 2^{20}=k\) 的答案即可。剩下的就是那种子集科技那种东西了。
考虑我们 \(\text{FFT}\) 在干什么
正常我们卷积的形式是 \(\sum\limits_{k=0}x^k\sum\limits_{i+j=k} a_{i}b_{j}\),我们 \(\text{FFT}\) 是把 \(w_{n}^{0} w_{n}^{1}\dots w_{n}^{n-1}\) 全部代入进去了,然后求出了答案,那么我们得到了这么多点值表示,然后 \(\text{TDFT}\) 就是再变成系数表示。
那么我们如果这样呢?\(\sum\limits_{k=0}x^k\sum\limits_{(i+j)\mod n=k} a_{i}b_{j}\),我们发现单位根是一个 \(\mod n\) 循环的一个东西。
考虑我们的 $ \(\text{FFT}\) $ 的数组长度,如果数组比较短的话我们这个就是循环卷积,为什么呢?我们 \(\text{FFT}\) 的长度就是我们选取的单位根的数量,我们之前 \(\text{FFT}\) 就是取的是比两个多项式长度加起来还长,所以我们取不取模都没有关系。那么如果我们取短一点比如就取最长的那个多项式的长度,那会发生什么事呢?首先我们 \(\text{DFT}\) 还是能算出来各个多项式的点值表示,然后再搞回去我们发现我们不会得到一个长度为 \(n+m\) 的多项式了,因为我们点值只有那么几个,我们会得到长度为初始设的那个长度的多项式。我们考虑每个点值对应什么,我们发现我们点值在代入多项式的时候大的次数的项就自动取模了!所以我们 \(\text{FFT}\) 做的是一个循环卷积。
那么回到这道题。那么 \(k=2\) 就是两个相同的多项式乘在一起了,所以我们就是把 \(k\) 个多项式乘一起即可,然后最后怎么搞,发现我们 \(s\) 肯定是一个二进制数的子集,然后我们就做完了。
Chirp Z-Transform 和 Bluestein
我们发现上面的 \(\text{FFT}\) 需要限制数组的长度,这会导致我们的单位根挂掉。
我们把单位根带进去看看
考虑到 \(2ij=(i+j)(i+j-1)-i(i-1)-j(j-1)\)
拆下指数
有什么用呢?我们原来长度为 \(n\) 做不了 \(\text{DFT}\) ,因为我们既要是 \(2\) 的幂次又要循环卷积。但是我们现在可以通过一次卷积来算出它的 \(\text{DFT}\)
我们把右边当作两个多项式的卷积,一个长度为 \(n+m\),为 \(\sum\limits_{i=0}^{n+m} w_{n}^{\frac{i(i-1)}{2}}\) 一个长度为 \(n\) 为 \(\sum\limits_{i=0}^{n} a_{i}w_{n}^{n-\frac{i(i-1)}{2}}\)。
此时我们可以暴力处理出来单位根,然后就完全得到了两个独立于外界的多项式。然后我们使用循环卷积,长度为 \(m\),那么我们得到的 \(m\) 项是不是就是我们的答案了?很奇怪啊,感觉假了,但是题解也没说啥。
确实错掉了,这个东西是减法卷积,我们如果第一个是 \(n+m\) 项,第二个是 \(n\) 项,原本的式子卷出来是 \(m\) 项,如果我们长度为 \(m\) 那么我们肯定错掉了。然后我们发现我们应该要把序列反过来做,因为这是一个减法卷积,如果正过来很难刻画这个卷积
我们观察最终序列的第 \(k\) 项,其为 \(i+j=k\) 卷起来,因为序列翻转,也就是 \(i+n-j=k\) 卷起来,那么就是 \(i-j=k-n\) 卷起来,所以我们把序列反转后取后 \(m\) 项即可。所以这里不用循环卷积,直接NTT即可
更多例题
P4841
考虑如何对这个图计数,考虑正难则反,我们考虑怎么样不会算重,这边先学EGF。
下面的推到中 \(n\) 都不指题目中的 \(n\),\(N\) 才是
图的总数显然是 \(2^{\frac{n(n-1)}{2}}\),那么设 \(f(n)\) 为不连通的 \(n\) 个点的图有多少个。考虑递推关系,\(f(n)=\sum\limits_{i=1}^{n-1} f(i)f(n-i)\),很容易发现算重了,然后这个有标号也没有很好体现。
计数题中,有标号就相当于有顺序,下面的式子中会体现。我们设 \(f(n)\) 为大小为 \(n\) 的无向图的个数,也就是 \(2^{\frac{n(n-1)}{2}}\),\(g(n)\) 为连通图的数量
时刻注意这里面标号的限制
我们还是考虑递推关系,然后我们枚举 \(1\) 所在联通块的大小,然后我们从另外 \(n-1\) 个点挑出 \(i-1\) 个点,将这些点都按到这个连通图上。
所以现在有个思路理解。我们的 \(g\) 和 \(f\) 都是有标号的,那么我们考虑证明递推出来也是有标号的。首先我们总的方案数是有标号的,然后对于那些不连通的图,我们希望我们全部减去了。考虑证明不重不漏,如果我们证明出来了,那自然就是有标号了。
不重显然,因为我们枚举保证了。考虑证明不漏,对于每一种方案,我们必定会遍历到,然后就证完了。
所以我们现在得到式子 \(f(n)=\sum\limits_{i=1}^{n} {n-1\choose i-1}g(i)f(n-i)=2^{\frac{n(n-1)}{2}}\) 考虑带入变形
然后考虑 \(\text{FFT}\) 的形式。我们这里的各种东西其实就是各种系数,我们乘起来的这两个东西的次数应该要一样
因为是入门,所以再写一点。我们上面的显然是卷积的形式,如果我们在题目中发现了这种形式,那么我们就可以把这两个乘起来的东西当作系数,然后我们右边就是对应次数的系数了。
然后上面这个应该错完了,因为我的式子好像只让一项的系数是对的,反正就是一点道理没有。我们多项式要每项的系数都对上才行。
我们上面的一个 \(\sum\) 就是一个系数,这个 \(n\) 也不是题目的 \(n\)。所以我们把和 \(i\) 有关的系数分给 \(g\),和 \(n-i\) 有关的系数分给另一个,\(n\) 分给对面即可。这个很重要!!!所以说变量重名非常致命啊!
然后推式子的时候一定要注意下标,最好就是不改变下标了,反正扔到板子里面就可以了
HDU4656
最后的式子我们将一个数组反转之后像 \(\text{chirp-Z}\) 那样搞就行了
LOJ556
还是之前的套路,但是我们发现貌似情况很多。
对于这种背包的问题,我们可以先将dp方程写出来,然后用多项式优化。
但是这里有数量为无限的物品,不好做,但是我们随便变一下就是数量有限了。那么我们每个阶段的 \(\text{OGF}\) 就是 \(\sum_{j=0}^{b_{i}} x^{j*a_{i}}\)。我们可以发现,我们 \(\text{OGF}\) 里的每一项都代表一个决策,这些决策在这个阶段是只能选一种的。
观察数据范围,挺大的,考虑变式子。
但是指数很大,考虑取对数
注意,我们泰勒展开时是先求导数,然后再把点带进去的,这个是宝宝级的东西了。
已知结论
那么我们的式子就是
那么我们发现我们只要知道每个指数贡献了多少次即可,那么我们搞一个桶,一个调和级数,然后把指数搞到多项式里面,然后最后 \(exp\) 一下即可。
CF438E
我们发现这个可以分成两部分,二叉树的形态和分配权值的方案。
但是大小相同的二叉树可能对应很多不同的权值分配。
先考虑大小为 \(s\) 的二叉树有多少个。考虑递推。\(dp[i]=2*dp[i-1]+\sum_{j=1}^{i-2} dp[j]*dp[i-1-j]\)。变成多项式更好处理的形式
\(dp[i]=\sum_{j=0}^{i-1} dp[j]*dp[i-1-j]\)
这个使用分治 \(\text{FFT}\) 即可
是不是不重不漏呢?不漏显然,不重因为每次枚举的时候都没重所以也显然。
显然做不下去,但是思路值得借鉴。考虑直接设 \(dp[i]\) 为权值为 \(i\) 的二叉树有多少个,这个显然比设大小为 \(i\) 好。设 \(a[i]\) 为 \(i\) 是否在集合中出现过。
所以答案显然是 \(dp[n]=\sum_{i=0}^{n} a[i]\sum_{j=0}^{n-i}dp[j]dp[n-i-j]\)
后面这个东西就是三个多项式卷起来。理解考虑三个多项式卷起来的每一项,然后一一对应即可。
那么我们设几个生成函数,然后发现 \(dp=a*dp*dp+1\)
在这里,因为我们 \(dp[0]=1\) 那么在 \(dp\) 的生成函数中我们要自动配齐这一项。
有两个解,考虑一手增根。
如果如果取加号,发现上面那个多项式 \(0\) 次项是 \(2\) 下面 \(0\) 次项是 \(0\),显然不成立,所以取减号。
貌似题解还要简单一点,也就是分子有理化