Q:你不是都退役了吗,为何还更博客?git
A:文化课太无聊了,我来更新更新。github
Q:不是说不想学多项式了吗,为何仍是学了?函数
A:下一周有浙大的校赛,而后发现咱们队里没有人会多项式,他们都去吃鸡了,因而就(把锅甩给我了)去学了。ui
Q:为何是准备浙大校赛,而不是准备ZJOIday2。spa
A:由于我day1被出题人卡常数送退役了,day2也没啥意义,并且出多项式我也必定拿不了什么分。code
Q:那你这么菜这篇文章有什么好看的。get
A:由于我数学和记性不太好,写下来帮助本身理解用的,想学多项式左转 txc好哥哥的博客。博客
都9102年了还有人不会 \(\text{FFT/NTT}\) 吗,想必任意模数也没啥用,就省略好了。数学
算了,仍是贴一个跑的很是慢的代码吧。it
inline void timesinit(int lenth){ for(len = 1, lg = 0; len <= lenth; len <<= 1, lg++); for(int i = 0; i < len; i++) rev[i] = (rev[i>>1] >> 1) | ((i & 1) << (lg - 1)); } inline void DFT(int *a, int sgn){ for(int i = 0; i < len; i++) if(i < rev[i]) swap(a[i], a[rev[i]]); for(int k = 2; k <= len; k <<= 1){ int w = ~sgn ? Pow(G, (P - 1) / k) : Pow(Pow(G, (P - 1) / k), P - 2); for(int i = 0; i < len; i += k){ int now = 1; for(int j = i; j < i + (k >> 1); j++, now = 1ll * now * w % P){ int x = a[j], y = 1ll * a[j+(k>>1)] * now % P; a[j] = (x + y) % P, a[j+(k>>1)] = (x - y + P) % P; } } } if(sgn == -1){ int Inv = Pow(len, P - 2); for(int i = 0; i < len; i++) a[i] = 1ll * a[i] * Inv % P; } } inline void times(int *a, int *b, int *c){ for(int i = 0; i < len; i++) c[i] = 1ll * a[i] * b[i] % P; }
如今有多项式 \(F(x)\) ,求一个多项式 \(G(x)\) ,知足 \(F(x)G(x)\equiv 1\) 。
考虑倍增,假设当前要求
\[ F(x)G(x)\equiv1(\bmod x^n) \]
而且已知
\[ F(x)G_0(x)\equiv1(\bmod x^\frac{n}{2}) \]
两式相减能够获得
\[ F(x)(G(x)-G_0(x))\equiv0(\bmod x^\frac{n}{2}) \\ G(x)-G_0(x)\equiv0(\bmod x^\frac{n}{2}) \\ \]
两边都平方能够获得
\[ G^2(x)-2G(x)G_0(x)+G_0^2(x)\equiv 0(\bmod x^n) \]
乘上 \(F(x)\) 获得
\[ G(x)-2G_0(x)+G_0^2(x)F(x)\equiv0(\bmod x^n) \\ G(x)\equiv G_0(x)(2-G_0(x)F(x)) (\bmod x^n) \]
一次操做须要两次 \(\text{DFT}\) ,复杂度 \(T(n)=T(\frac{n}{2})+\mathcal O(n\log n)=\mathcal O(n\log n)\) 。
inline void getinv(int *a, int *b, int n){ if(n == 1) return (void) (b[0] = Pow(a[0], P - 2)); getinv(a, b, (n + 1) / 2); static int tmp[N]; timesinit(n * 2 - 1); for(int i = 0; i < len; i++) tmp[i] = i < n ? a[i] : 0; DFT(tmp, 1), DFT(b, 1); for(int i = 0; i < len; i++) b[i] = 1ll * (2 - 1ll * tmp[i] * b[i] % P + P) % P * b[i] % P; DFT(b, -1); for(int i = n; i < len; i++) b[i] = 0; for(int i = 0; i < len; i++) tmp[i] = 0; }
和普通的牛顿迭代形式同样,从多项式的角度推一下
咱们有关于多项式 \(F(x)\) 的方程 \(G(F(x))=0\),而且假设当前已经求出
\[ G(F_0(x))\equiv0\ (\bmod x^{n}) \]
在这基础上咱们要求 \(G(F(x))\equiv 0(\bmod x^{2n})\) 。
对 \(G(F(x))\) 在 \(F_0(x)\) 处泰勒展开,能够获得
\[ G(F(x))=\sum_{i}\dfrac{G^i(F_0(x))}{i!}(F(x)-F_0(x))^i \]
其中 \(G^i\) 表示 \(G\) 的 \(i\) 阶导函数。
因为 \(F(x)-F_0(x)\) 的前 \(n\) 项系数都是 \(0\) ,当 \((F(x)-F_0(x))^i\) 的次数大于 \(1\) 时,所得多项式的系数都是 \(0\) ,由于卷积的时候每一次乘法都至少有一个系数为 \(0\) 的项参与,因此能够获得
\[ G(F(x))\equiv G(F_0(x))+G'(F_0(x))(F(x)-F_0(x))\equiv 0\ (\bmod x^{2n}) \\ F(x)\equiv F_0(x)+\dfrac{G(F(x))-G(F_0(x))}{G'(F_0(x))} (\bmod x^{2n}) \]
由于 \(G(F(x))\equiv 0 (\bmod x^{2n})\),因此有
\[ F(x)\equiv F_0(x)-\dfrac{G(F_0(x))}{G'(F_0(x))} (\bmod x^{2n}) \]
能够用来推不少东西,开根,exp,三角函数什么的,好像不能用来推求逆,推出来的式子要用到求逆。
如今有一个多项式 \(F(x)\),要求多项式 \(G(x)\equiv \sqrt{F(x)}\ (\bmod x^m)\) 。
即
\[ G(x)^2 =F(x)\\G(x)^2-F(x)=0 \]
假设当前已经有 \(G_0(x)^2-F(x)\equiv0\ (\bmod x^n)\) ,要求 \[G(x)^2-F(x)\equiv0\ (\bmod x^{2n})\]
套用牛顿迭代
\[ G(x)\equiv G_0(x)-\dfrac{G_0(x)^2-F(x)}{2G_0(x)}\ (\bmod x^{2n}) \\ G(x)\equiv \dfrac{1}{2}\left( G_0(x)-\dfrac{F(x)}{G_0(x)}) \right) \]
须要保证 \(F(x)\) 的常数项有二次剩余。
复杂度根据主定理 \(T(n)=T(\dfrac{n}{2})+\mathcal O(n\log n)=\mathcal O(n \log n)\) ,每次须要一遍乘法一遍求逆 。
inline void getsqrt(int *a, int *b, int n){ static int tmp1[N], tmp2[N]; if(n == 1) return (void) (b[0] = 1); getsqrt(a, b, (n + 1) / 2); for(int i = 0; i < n; i++) tmp1[i] = a[i]; getinv(b, tmp2, n), timesinit(n * 2 - 1); DFT(tmp1, 1), DFT(tmp2, 1); for(int i = 0; i < len; i++) tmp1[i] = 1ll * tmp1[i] * tmp2[i] % P; DFT(tmp1, -1); for(int i = 0; i < len; i++) b[i] = 1ll * (b[i] + tmp1[i]) % P * Pow(2, P - 2) % P; for(int i = n; i < len; i++) b[i] = 0; for(int i = 0; i < len; i++) tmp1[i] = tmp2[i] = 0; }
如今有一个多项式 \(F(x)\) ,要求 \(\ln(F(x))\)。
\[ \ln(F(x))=\int (\ln(F(x)))' \\ =\int (\ln'(F(x))\cdot F'(x))\\ =\int\dfrac{F'(x)}{F(x)} \]
多项式求逆+多项式求导+多项式积分便可
多项式求逆复杂度 \(\mathcal O(n\log n)\),求导和积分下面式子来就行了,都是其中较为简单的。
\[ F(x)=\sum_{i\geq0}A_ix^i \\ F'(x)=\sum_{i\geq0}iA_{i+1}x^i \]
因此总的复杂度也是 \(\mathcal O(n\log n)\),注意积分之后是 \(F(x)+c\) 的形式,要保证 \(F(x)\) 常数项为 \(1\)。
inline void getln(int *a, int *b, int n){ static int tmp[N]; getinv(a, b, n), timesinit(n * 2 - 1); for(int i = 1; i < n; i++) tmp[i-1] = 1ll * a[i] * i % P; for(int i = n - 1; i < len; i++) tmp[i] = 0; DFT(tmp, 1), DFT(b, 1); for(int i = 0; i < len; i++) b[i] = 1ll * tmp[i] * b[i] % P; DFT(b, -1); for(int i = len - 1; i; i--) b[i] = 1ll * b[i-1] * Pow(i, P - 2) % P; b[0] = 0; for(int i = n; i < len; i++) b[i] = 0; for(int i = 0; i < len; i++) tmp[i] = 0; }
如今有一个多项式 \(F(x)\) ,要求 \(G(x)\equiv e^{F(x)}\ (\bmod x^m)\)。
先取一下对数
\[ \ln(G(x))-F(x)=0 \]
假设当前已经有 \(\ln(G_0(x))-F(x)\equiv0\ (\bmod x^n)\) ,要求 \[\ln(G_0(x))-F(x)\equiv0\ (\bmod x^{2n})\]
直接套用牛顿迭代
\[ G(x)\equiv G_0(x)- G_0(x)(\ln(G_0(x))-F(x))\ (\bmod x^{2n})\\ G(x)\equiv G_0(x)(1-\ln(G_0(x))+F(x))\ (\bmod x^{2n}) \]
须要保证 \(F(x)\) 的常数项为 \(0\) ,求出来的 \(G(x)\) 的常数项必定为 \(1\)。
复杂度根据主定理 \(T(n)=T(\dfrac{n}{2})+\mathcal O(n\log n)=\mathcal O(n \log n)\) ,每次须要一遍乘法一遍 \(\ln\) 。
inline void getexp(int *a, int *b, int n){ static int tmp[N]; if(n == 1) return (void) (b[0] = 1); getexp(a, b, (n + 1) / 2); getln(b, tmp, n), timesinit(n * 2 - 1); for(int i = 0; i < n; i++) tmp[i] = (!i - tmp[i] + a[i] + P) % P; DFT(tmp, 1), DFT(b, 1); for(int i = 0; i < len; i++) b[i] = 1ll * b[i] * tmp[i] % P; DFT(b, -1); for(int i = n; i < len; i++) b[i] = 0; for(int i = 0; i < len; i++) tmp[i] = 0; }
如今有一个多项式 \(F(x)\) ,要求 \(G(x)\equiv F(x)^k (\bmod x^m)\)
若是 \(F(x)\) 的常数项为 \(1\) ,那么有
\[ F(x)^k=\exp(k\ln(F(x))) \]
不然须要把 \(F(x)\) 的常数项变成 \(1\) ,找到最低的不为 \(0\) 的项的系数 \([x^p]\) ,先让多项式除以 \([x^p]x^p\) 这个单项式,而后就变成了一个常数项为 \(1\) 的多项式 \(F_0(x)\)。
最后再把这个单项式乘回来,有
\[ F(x)=[x^p]^kx^{pk}F_0(x) \]
须要一遍 \(\ln\) 和一遍 \(\exp\) ,复杂度 \(\mathcal O(n\log n)\) ,须要判的细节略微有点多。
inline void power(int *a, int *b, int n, int m, ll k){ static int tmp[N]; for(int i = 0; i < m; i++) b[i] = 0; int fir = -1; for(int i = 0; i < n; i++) if(a[i]){ fir = i; break; } if(fir && k >= m) return; if(fir == -1 || 1ll * fir * k >= m) return; for(int i = fir; i < n; i++) b[i-fir] = a[i]; for(int i = 0; i < n - fir; i++) b[i] = 1ll * b[i] * Pow(a[fir], P - 2) % P; getln(b, tmp, m); for(int i = 0; i < m; i++) b[i] = 1ll * tmp[i] * (k % P) % P, tmp[i] = 0; getexp(b, tmp, m); for(int i = m; i >= fir * k; i--) b[i] = 1ll * tmp[i-fir*k] * Pow(a[fir], k % (P - 1)) % P; for(int i = 0; i < fir * k; i++) b[i] = 0; for(int i = 0; i < m; i++) tmp[i] = 0; }