多项式大学习
泰勒展开
感觉好有用啊,学一下
其实很简单。数学中,泰勒公式是一个用函数在某点的信息描述其附近取值的公式。如果函数足够平滑的话,在已知函数在某一点的各阶导数值的情况之下,泰勒公式可以用这些导数值做系数构建一个多项式来近似函数在这一点的邻域中的值。泰勒公式还给出了这个多项式和实际的函数值之间的偏差。
泰勒公式是将一个在 \(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}\) 处对左右求导就行了。因为 \(x=x_{0}\),所以很多项都消掉了
麦克劳林展开式:
麦克劳林展开是泰勒展开在 \(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\) 项的系数上即可

然后求个二次剩余就做完了。
EGF
强大
从 OGF 到 EGF
对比一下OGF吧。
OGF:只关心数量,而不关心具体是哪些球,因为球之间没有区别。
EGF:有标号。这些物品本身还有组织方式(比如一个排列、一个循环、一个图)就是这些带标号物品的一种组织方式。
定义
比如对于数列 \(0!,1!,2!\) 我们的 \(E(x)=x^{0}+x^{1}+x^{2}+\dots=\frac{1}{1-x}\)
现在让我们看看 \(EGF\) 的卷积
假设 \(C=A*B\)
!这里有个组合数,nice
例子
引理1
设 \(c_{n}\) 是大小为 \(n\) 的具有特定结构的东西的方案数,比如说,\(c_{n}\) 可以是循环排列的数量这种东西。那么我们还会有东西 \(s_{n}\),代表很多独立的特殊结构组成的大小为 \(n\) 的集合。比如说我们有两个的循环排列的集合就可以被算进去什么东西。
那么假设我们知道了 \(c_{n}\),直觉告诉我们,现在它的 \(s_{n}\) 就和 \(c\) 本身无关了,我们尝试推导一下。
很好理解
设 \(E(x)=\sum \frac{s_{n}}{n!}x^{n}\)
观察一下内层的求和,发现我们相当于遍历了所有大小的情况,然后有 \(k\) 个集合。
发现 \(e^{E_{c}(x)}=E_{s}(x)\)
这个对两边泰勒展开就行了
引理2
\(1,a,a^{2}\) 的 \(EGF\)。\(E(x)=\sum \frac{a^{i}}{i!} x^{i}\)
这个哪个神人的泰勒展开?
发现是 \(e^{ax}\)。证明一下,这个东西的一阶导就是 \(ae^{ax}\),二阶导就是 \(a^{2}e^{ax}\),然后就证好了
贝尔数
将 \({1,2,\dots,n}\) 划分为若干个非空子集的方案数 \(w_{n}\) 称为贝尔数。
直接上,我们枚举1所在的集合就可以得到一个递推关系。\(w_{n}=\sum_{i=1}^{n} {n-1\choose i-1}w_{n-i}\)
我还有一种方法,根据上面的引理,我们只要知道了 \(c\) 的 \(EGF\) 就行了,这个东西会好求很多。
\(E(x)=\sum \frac{c_{i}}{i!} x^{i}\)
\(c_{i+1}=\sum_{j=0}^{i} {i\choose j}c_{j}\)
\(\sum c_{i+1}\frac{x^{i}}{i!}=\sum \sum_{j=0}^{i} {i\choose j}c_{j} \frac{x^{i}}{i!}\)
右边=\(\sum_{j=0}c_{j} \sum_{i=j} {i\choose j} \frac{x^{i}}{i!}\)
后面那个东西就是 \(e^{x}\)
对比一下左右,发现 \(\sum c_{i+1}\frac{x^{i}}{i!}=\sum_{j=0}c_{j} \frac{1}{j!}x^{j}e^{x}\)
左边是不是某个东西的导数?
这个东西根本不会做。不会积分qwq
深入理解!
我们发现我们上面的 \(c_{n}\) 其实代表大小为 \(n\) 的这种特殊结构有多少种。因为集合是无序的,所以答案就是1了。
所以这个序列的 \(EGF\) 就是 \(E(x)=\sum_{i=1} \frac{x^{i}}{i!}\)
然后我们贝尔数的 \(EGF\) 就是 \(exp(e^{x}-1)\)
真的太搞笑了
和多项式综合
生成函数也是多项式,在利用生成函数推式子的时候我们会利用多项式科技去优化
CF958F3
这题不用组合数,所以我们使用OGF
我们给每种颜色搞一个 OGF,假设这个颜色有 \(b_{i}\) 个球
然后我们把所有 \(F(i)\) 乘起来,然后取 \(x^{k}\) 的系数即可
我们发现我们这样卷会T
利用线段树那种方法来合并,那么每个多项式只会被合并 \(\log\) 次,然后就做完了
据说答案用long long可以存下来?任意模数也可以过的。
P5401
差不多就是有多少变量的情况,使得变量中至少有m对相同的数。
有m对相同的数?直接组合数枚举。这个肯定重了吧。
设dp[i][j]为已经做了i个变量,之前没有匹配的数大小不同的有j个已经匹配了k个的情况数
新加入的这个数,如果和前面相同,那么,否则,状态是好转移的。
一点都转移不了,我们消掉一个数之后我们该怎么办呢?有可能这个数还有剩下的,
不会的不会的,只会剩下一个
28分拿下。看看n^2
首先我们可以优化一波状态?
寄。
发现重要性质!我们最终配对的数是\frac{n-出现次数为奇数次的数的数量}{2},很好理解,因为出现次数只能为奇或者为偶,这样我们的dp可以优化掉一维。
如果d特别小我们怎么做?枚举出现的次数?大概也是不太行的
我们枚举我们dp方程的第二维 \(\sum_{i=0}^{n-2*m} dp[i]\)。dp[i]代表奇数个数恰好为i的有多少个。
恰好?我靠,我二项式反演的式子基本都忘掉了
显然,恰好有多少个,这个东西是可以用二项式反演的。
先特判n-2m<0和>d的情况,简单的
设fi为奇数个数至少为i个的情况数
\(fi=\sum_{j=i}^{d} {j \choose i} dp[i]\)
\(dp[i]=\sum_{j=i}^{d} f_{j} (-1)^{j-i} {j\choose i}\)
这个东西是一个卷积形式,我们先提出和 \(i\) 有关的项
然后翻转一下 \(j-i\) 变成 \(i-j\) 即可。
现在我们只要求出 \(f\) 即可。
首先我们搞出两个序列的生成函数,一个是 \({1,1,1,\dots}=e^{x}\)
一个是 \(0,1,0,1,0,\dots\)
我们对于第二个证一下,
\(E(x)=\sum \frac{a_{i}}{i!}x^{i}\)
\(E(x)=\sum \frac{1}{(2i+1)!}x^{2i+1}\)
这又是哪个神仙的泰勒展开?
发现 \(\frac{e^{x}-e^{-x}}{2}\) 就是,这个东西利用复合函数求导可以得到 \(e^{-x}\) 的一阶导数是 \(-e^{-x}\) 然后就好了。
所以我们的 \(E(x)=\frac{e^{x}-e^{-x}}{2}\)
然后现在我们看看 \(f\) 要怎么搞出来。对于 \(f_{i}\),我们就是要从 \(d\) 中挑 \(i\) 个数钦定为奇数。剩下的随便。多项式乘法,每个括号代表一个选择,所以我们前 \(i\) 个括号就是选奇数,后 \(d-i\) 个括号就是任意选,所以我们的答案就是 \(f_{i}={d\choose i}n!((\frac{e^{x}-e^{-x}}{2})^{i}(e^{x})^{d-i})[x^{n}]\)
最后一个括号代表我们取 \(x^{n}\) 的系数。
这里我们为什么要 \(EGF\) 呢?这其实代表了我们是有标号选的。确实,这里就是有标号的。
\(f_{i}=\frac{{d\choose i}n!}{2^{i}}((e^{x}-e^{-x})^{i}(e^{x})^{d-i})[x^{n}]\)
二项式展开
\(f_{i}=\frac{{d\choose i}n!}{2^{i}}\sum_{j=0}^{i} {i\choose j}e^{xj}(-e^{-x})^{i-j}e^{x(d-i)}[x^{n}]\)
\(f_{i}=\frac{{d\choose i}n!}{2^{i}}\sum_{j=0}^{i} {i\choose j}(-1)^{i-j}e^{xj-x(i-j)+x(d-i)}[x^{n}]\)
\(f_{i}=\frac{{d\choose i}n!}{2^{i}}\sum_{j=0}^{i} {i\choose j}(-1)^{i-j}e^{x(2j-2i+d)}[x^{n}]\)
\(f_{i}=\frac{{d\choose i}n!}{2^{i}}\sum_{j=0}^{i} {i\choose j}(-1)^{i-j}e^{x(2j-2i+d)}[x^{n}]\)
右边这个东西是一个 \(EGF\),主要就是因为这个 \(e\)。所以我们用引理二给它拆开来,它的第 \(n\) 项就是。\(\frac{(2j-2i+d)^ {n}}{n!}\)
\(f_{i}=\frac{{d\choose i}n!}{2^{i}}\sum_{j=0}^{i} {i\choose j}(-1)^{i-j}\frac{(2j-2i+d)^{n}}{n!}\)
\(f_{i}=\frac{{d\choose i}i!}{2^{i}}\sum_{j=0}^{i} \frac{1}{j!(i-j)!}(-1)^{i-j}(2j-2i+d)^{n}\)
\(f_{i}=\frac{{d\choose i}i!}{2^{i}}\sum_{j=0}^{i} \frac{1}{j!(i-j)!}(-1)^{i-j+n}(2i-2j-d)^{n}\)
所以一个多项式是 \(\sum \frac{1}{j!}\)
一个多项式是 \(\frac{1}{(i-j)!}(-1)^{i-j+n}(2i-2j-d)^{n}\)
我们算完之后就知道了 \(f\),我们再卷积一下就可以了
P10004
排列?
注意到,我们这里是恰好有x个这样的东西。这就是二项式反演了,然后我们直接来一波二维容斥。
设fi,j为至少有i,j个这样的东西的方案
\(ansi,j=\sum {a\choose i}{b\choose j} f(a,b)*(-1)^{a+b-i-j}\)
\(ansi,j=\sum \frac{a*\dots*(a-i+1)*b*\dots*(b-j+1)}{i!j!} f(a,b)*(-1)^{a+b-i-j}\)
\(ansi,j=\frac{1}{i!j!}*(-1)^{-i-j}\sum a*\dots*(a-i+1)*b*\dots*(b-j+1) f(a,b)*(-1)^{a+b}\)
求这个东西,我们考虑递推,比如我们从ansi+1,j推过来,相当于加上一行,然后前面的。
发现这样还是有点难搞,我们处理出来每一行的前缀和也就是说,对于一行,我们假如要贡献到j列,这个显然是很好算的,所以我们把这个东西前缀和搞一下。具体而言,我们对于每个(i,j)都暴力算一下,时间 \(O(n^3)\)
然后做ansi,j的时候枚举行大力算就行了,前面那个系数随便算一下
现在就是要算f了。
先让我们考虑一个子问题,对于一个长度为n的排列,假设我们钦定有i个上升的东西,这怎么算?考虑排列dp,排列dp,性质不知有你插入的位置这种东西,还有一个很重要的性质就是你放入的数是当前最大的。因为这个是上升的。所以我们放的位置有可能是1的贡献,有可能是0的贡献。发现贡献和当前上升的数量有关
我们设dpi,j表示现在做到i,上升的有j个,那么转移是显然的
然后我们就利用O(n^2)的时间做完了这个问题
但是还不够,我们可以线性的吧?
发现了一道弱化版?P5825
继续考虑这个东西
然后考虑转化一下。我们发现有k个升高,那么我们这个序列其实就被分成了n-k个递增的连续序列。这个条件就比较像人了一点点。
恰好有k个?直接二项式反演给它搞了。这东西应该可以二项式反演的,欸?可以吗?
可以的。现在我们要求至少有k个的。也就是我们枚举每个的长度,然后把数分配一下,这个东西是简单的。所以我们 \(f(k)\) 代表一段长度为 \(k\) 的上升序列有多少种方案,显然只有1种方案。所以EGF就是 \(e^{x}-1\)。然后把EGF乘到一起就是答案了。
m个上升序列,n个数字,那么答案是 \((e^{x}-1)^{m}[x^{n}]\),稍微推一下式子,很简单
这个东西显然是卷积
然后二项式反演,\(=\sum_{j=i} f_{j}*{j\choose i}*(-1)^{j-i}\)
这个东西显然还是一个卷积
但其实我们还可以再化简一下式子
省略一下推式子的过程,主要是要证明一个东西,也就是 \(\sum_{i=k}^{n} {i\choose k}{n-i\choose j}\)
这个的意义?从 \(n+1\) 中选出 \(k+j+1\) 个数的方案数,我们枚举 \(k+1\) 这个数的大小位置,然后前面选一点,后面选一点。
这给我们的组合推导提供了启发,比如说看到这种两个组合数乘一起求和的,通常,我们求和的下标是我们枚举的东西,并把我们选择的集合分成了两个部分
所以最终的式子是 \(ans_{k}=\sum_{j=0}^{n-k} (-1)^{n-k-j}j^{n}{n+1\choose k+j+1}\)
\(ans_{k}=(n+1)!\sum_{j=0}^{n-k} (-1)^{n-k-j}j^{n}\frac{1}{(k+j+1)!(n-k-j)!}\)
\(ans_{k}=(n+1)!\sum_{j=0}^{n-k} \frac{(-1)^{n-k-j}}{(n-k-j)!}\frac{j^{n}}{(k+j+1)!}\)
\(ans_{k}=(n+1)!\sum_{j=0}^{n-k} \frac{1}{(j+k-n)!}\frac{j^{n}}{(k+j+1)!}\)
行吧,真的这式子都推这么久
回到这题。我们的逆排列的意义就是大小为i的数出现在哪里?
我们考虑排列中的一个递增的序列。我们发现大小越大的数出现的位置也越大,所以说,我们原排列一个递增的序列是按照顺序被分配到逆排列中的若干个递增序列的。
我们现在钦定了有 \(i,j\) 个上升的连续段,设 \(c_{x,y}\) 为原排列第 \(x\) 个连续上升的段有 \(c_{x,y}\) 个分到了 \(y\) 个连续段了。
这样我们就把方案映射到了一个矩阵,现在我们要给它填数,然后求方案数,然后还有一些限制。首先每行每列和必须为整数,因为一个连续段不能为空,然后矩阵的总和为 \(n\)。
那么一个矩阵的方案代表多少种方案呢?现在考虑证明这个矩阵和这个排列的双射
对于一个已经确定的排列,我们就能确定一个矩阵。现在就是一个矩阵确定一个唯一的排列。现在我们能确定各个连续段的大小,也就是说,我们能知道大小为[xi,yi]的位置是递增的,然后下标为[li,ri]的大小是递增的。我们首先确定大小为1的在那里。首先我们的1肯定是作为了下标为[li,ri]递增区间的开头了。然后现在我们知道了[1,x]下标是递增的。那么x+1肯定排在了1的前面。
设这个矩阵的数量为 \(t_{i,j}\)。先考虑只有和为 \(n\) 的限制的矩阵数量 \(g_{i,j}\),这相当于我们有 \(n\) 个小球,要分配到 \(ij\) 个不同的桶里,直接插板法,答案为 \({n+ij-1\choose ij-1}\)。
加上限制,自然就是容斥了 \(t_{i,j}=\sum_{i1=0}^{i} \sum_{j1=0}^{j} {i\choose i1}{j\choose j1}\frac{(n+i1j1-1)!}{(i1j1-1)!n!}*(-1)^{i+j-i1-j1}\)
这个是 \(n^{4}\) 的,还是考虑之前的手法。
\(t_{i,j}=\sum_{i1=0}^{i} \sum_{j1=0}^{j} {j\choose j1}*(-1)^{j}{i\choose i1}\frac{(n+i1j1-1)!}{(i1j1-1)!n!}*(-1)^{i-i1-j1}\)
\(t_{i,j}=\sum_{j1=0}^{j} {j\choose j1}*(-1)^{j}\sum_{i1=0}^{i} {i\choose i1}\frac{(n+i1j1-1)!}{(i1j1-1)!n!}*(-1)^{i-i1-j1}\)
后面的式子遍历 \(i\) 的时候预处理
但是问题在于,如何构建矩阵和排列的双射呢?确实这题构建双射是挺困难的。
我们上面的证明显然没有用完所有的限制,我们这个东西很强,是每个上升段之间都有分配。比如说对于原排列的第一个上升段,它分配给了逆排列的一些上升段。也就是说,大小为这么一些的编号是连续的。我们可以知道这个连续段的大小。然后根据这个,我们就大概会搞出这么样子的东西

然后加上逆排列的东西

其中,每条斜线都代表一个下标+1,数+1的东西,这样我们的数集就确定的。所以我们的双射就构建完毕了,这题也就结束了
拉格朗日插值:
直接有公式
\(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
圆!
求这个的期望可以分为两部分,一个是求有多少颗生成树,还有一个就是求这些生成树的权值之和。因为这个是完全图,那么这里面所有边都应该是对称的,考虑每一条边出现的次数。总共有 \(n^{n-2}\) 条颗生成树,所以有 \((n-1)n^{n-2}\) 条边,完全图总共有 \(\frac{n(n-1)}{2}\) 条边,那么每条边就出现了 \(2n^{n-3}\) 次,那么答案就是 \(2n^{n-3}\sum_{i=1}^{n-1} \sum_{j=i+1}^{n} (i+j)^{k}\)。
后面的比较难求,我们有一个 \(O(n\log k)\) 做法做后面的东西,注意到后面应该是一个 \(k+1\) 次的多项式,考虑插值,
\(f(k) = \sum\limits_{i=0}^{n} y_i \prod\limits_{j \neq i} \frac{k - j}{i - j}\)
如果取值连续,我们直接处理前缀积和后缀积即可,这样我们就可以做到 \(O(k\log k)\) 做了。
发现瓶颈在于求这个函数。
这个函数我们稍微差分一下,发现 \(ans_{i}=ans_{i-1}+\sum_{j=i+1}^{2*i-1} j^{k}\)
所以瓶颈就在求后面的 \(j^{k}\) 这个在线性筛的途中就可以求出来了。要插 \(k+3\) 个函数
P5667
还是重心拉格朗日插值!
由于给出的点值是连续
\(f(m+x)=\sum_{i=0}^{n} y_{i} \prod_{j\neq i} \frac{m+x-j}{i-j}\)
考虑递推,变化的之后第二个式子上面的东西,这个相当于每次每项都要乘上一个分数。首先用一个数组存下来各个东西
\(f(m+x)=f(m+x-1)+\sum_{i=0}^{n} y[i]*\frac{m+x}{m-1}\)
然后就做完了。其实后面那个东西是求和号。我们表示成阶乘的形式
\(f(m+x)=\sum_{i=0}^{n} y_{i} \sum_{j\neq i} \frac{m+x-j}{i-j}\)
不是啊?这个东西因为有那个 \(i\neq j\) 所以每次我们每个y都会分为两部分来变化,这些是不一样,然后就不行了。
还是看看式子
\(f(m+x)=\sum_{i=0}^{n} y_{i} \frac{(m+x)!}{(m+x-n-1)!(m+n-i)i!(n-i)!(-1)^{n-i}}\)
把常数项和和 \(x\) 有关的项这些提出来,我们把其它的处理处理出来,在最后搞上这些东西
\(f(m+x)=\frac{(m+x)!}{(m+x-n-1)!}\sum_{i=0}^{n} \frac{y_{i}}{(m+x-i)i!(n-i)!(-1)^{n-i}}\)
扔掉,提出只和 \(i\) 有关的 \(f(m+x)=\sum_{i=0}^{n} \frac{y_{i}}{i!(n-i)!(-1)^{n-i}}\frac{1}{m+x-i}\)
发现卷积形式。
设 \(p\) 为:
\(q\) 为:
我们设计的话第 \(i\) 项是那个的话,我们的 \(x-i\) 项就要是 \(\frac{1}{m+x-i}\) 那么我们第 \(i\) 项就是 \(\frac{1}{m+i}\) 这样的话,我们取后面的几位就可以了
多项式科技
单位根
这个东西是虚数,虚数的乘法是模长相乘,辐角相加。
\((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}\) 的性质。主要的思想就是利用了从小到大把下标的二进制位搞清楚,这样就很方便分讨
大概的思想就是构造一个函数,使得两个多项式乘起来的点值表达式是两个多项式的点值表达式按位乘起来。那么我们分别dft之后,然后再乘起来,然后再idft,就可以还原了。
或卷积
\(\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}\),然后中间显然是可以乘起来的,然后就可以了
首先我们构造了一个函数,使得两个多项式乘起来的点值表达式是两个多项式的点值表达式按位乘起来。使得更深入的理解,我们每次挑一层容斥。我们每一层考虑二进制,如果二进制为1,那么在dft里我们就要把前面的位置算上,显然这个交换层之间的顺序是没有关系的。在idft中,我们就是把所有操作都倒过来,也很好理解
与卷积
仿照上面,\(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}\) 都不会改变,那么我们直接加上后面的所有答案即可。对于后面,我们加上最高位之后,前面还是不会改变的,对于后面,前面的贡献还是一样的,后面的会变成相反数,然后就做完了。
还原还是倒过来
这里比较难理解。多写一点。做到第 \(i\) 层的时候,我们要算上下面那层的答案,对于 \(i-1\) 层,我们这层要合并两个的答案,那么我们下面的1,就会对上面的0贡献正值,对上面的1贡献负值。
子集卷积
其中每个限制都是好做的,所以我们考虑拆成两维的一个东西,第一维是值,第二维是 \(\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}}\)
upd on 20225/11/16
牛顿迭代其实很高深,今天试着能不能搞懂
给定一个关于 \(x,f(x)\) 的多项式 \(G(x,f(x))\)
这个东西其实是二元的多项式,无非是把第二个元通过一个多项式映射成了 \(f(x)\)
我们 \(G(x)\) 的形式大概有这种东西 \(xF(x)\)
比如还是用多项式求逆举例子
给定多项式 \(A(x)\),求 \(B(x)\),使得 \(A(x)B(x)\equiv 1\pmod x^n\),也就是只有常数项为 \(1\),其它的系数都是 \(0\)。
设 \(G(x,B(x))=A(x)B(x)-1\)
然后我们直接求 \(G\) 的零点,求出来就知道 \(B(x)\)
然后引入一个东西,偏导数
对于只有一个变量的函数,我们变化率只有一种。对于多个变量的函数,我们的变化率有很多种,所以我们挑一个,把其它看作常数?这个东西的意义就是告诉我们这个函数关于 \(x\) 的变化率是多少。
那为什么对 \(B(x)\) 求导呢?因为 \(x\) 是确定的,我们的 \(B(x)\) 是未知的,我们要确定 \(B(x)\),所以我们的牛顿迭代在不断更新我们的 \(B(x)\)
那么牛顿迭代的过程是什么呢?
考虑倍增。
首先当 \(n=1\) 时,\(\left[x^{0}\right]G\left(x, f\left(x\right)\right)=0\) 的解需要单独求出,假设中的 \(f_1\) 即为一个解。
假设现在已经得到了模 \(x^{\left\lceil\frac{n}{2}\right\rceil}\) 意义下的解 \(f_{\left\lceil\frac{n}{2}\right\rceil}\left(x\right)\),要求模 \(x^{n}\) 意义下的解 \(f\left(x\right) = f_n\left(x\right)\)。
将 \(G\left(x, f(x)\right)\) 对 \(f(x)\) 在 \(f(x)=f_{\left\lceil\frac{n}{2}\right\rceil}\left(x\right)\) 处进行泰勒展开,有:
解释一下这个操作在干啥。我们是知道了 \(G\) 的系数是 \(0\) 了,然后利用泰勒展开表示出这个系数,从而得到 \(f(x),f_{\left\lceil\frac{n}{2}\right\rceil}\left(x\right)\) 之间的等式的。这也是我们对 \(f(x)\) 求偏导的原因。
因为 \(f\left(x\right)-f_{\left\lceil\frac{n}{2}\right\rceil}\left(x\right)\) 的最低非零项次数最低为 \(\left\lceil\frac{n}{2}\right\rceil\),故有:
则:
套到多项式求逆里面
把 \(\frac{1}{A(x)}\) 替换成 \(B_{\left\lceil\frac{n}{2}\right\rceil}(x)\) 就得到我们最终的式子了。
然后就可以不用脑子很多了
多项式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即可

浙公网安备 33010602011771号