多项式相关操作学习笔记

多项式相关操作学习笔记

标签: 多项式


说在前边

记录一下相关的多项式操作,顺便存个模板。(多点求值之后的部分,有点写不动了。。。留坑留坑


多项式


定义

给定一个环\(R\)(\(R\)通常是交换环,可以是有理数、实数或者复数等等)以及一个未知数\(X\),则任何形同:$$a_0 + a_1X + ... +a_{n-1}X^{n-1} + a_nX^n$$的代数表达式叫做\(R\)上的一元多项式。其中\(a_0,a_1,... ,a_n\)\(R\)中的元素。未知数不代表任何值,但环 \(R\)上的所有运算都对它适用。在不至于混淆的情形下,一般将一元多项式简称为多项式。可以证明,两个多项式的和、差与积仍然是多项式,即多项式组成一个环 \(R[X]\),称为\(R\)上的(一元)多项式环。—— From Wiki


多项式乘法

计算两个多项式相乘时,首先使用乘法对加法的分配律将各项拆出,然后运用乘法结合律整合每一项,最后和加法一样整合同类项,就能得到乘积多项式,形式化的说:

\[P = a_0 + a_1X + ... +a_{n-1}X^{n-1} + a_nX^n \\ Q = b_0 + b_1X + ... +b_{n-1}X^{n-1} + b_nX^m \\ PQ = \sum_{i+j=0}a_ib_j +\sum_{i+j=1}a_ib_jX + ... + \sum_{i+j=n+m}a_ib_jX^{n+m} \]


离散傅里叶变换(DFT)

定义

对于\(N\)点序列\(x[n]\),它的\(DFT\)为:

\[X[k] = \sum_{n = 0}^{N-1}e^{-i\frac{2\pi}{N}nk}x[n] ~~k = 0,1,...,N-1\]

离散傅里叶变换的逆变换(IDFT)为:

\[x[n] = \frac{1}{N}\sum_{k = 0}^{N-1}e^{i\frac{2\pi}{N}nk}X[k]~~n = 0,1,...,N-1 \]


DFT与圆周卷积

  1. 圆周移位
    \(f(n) = x((n+m))_NR_N(n)\),成\(f(n)\)\(x(n)\)\(m\)点圆周移位序列。
    步骤:1)将x(n)以N为周期进行周期延拓;2)移位m点;3)取主值序列

  2. \(x(n), y(n)\)进行DFT,\(x(n) \leftrightarrow X(k), y(n) \leftrightarrow Y(k)\)

\[F(k) = X(k)Y(k) \leftrightarrow f(n) = \sum_{m=0}^{N-1}x(m)y((n-m))_NR_N(n) \]


有限长序列的圆周卷积与线性卷积

\(x(n)\):\(M\)点序列,\(y(n)\):\(N\)点序列

线性卷积:$$x(n)*y(n) = \sum_{m=-\infty}^{\infty} x(m)y(n-m)$$

可知\(f(n)\)的非\(0\)区间为\([0,M+N-2]\)

圆周卷积是线性卷积以\(L\)为周期延拓后,取\([0,L-1]\)间的主值序列。
\(L \geq N+M-1\) 时,圆周卷积等价于线性卷积


DSP角度的解释

从DTFT到DFS,从DFS到DFT,从DFT到FFT,从一维到二维


一些变换

快速傅里叶变换(FFT)

很久之前参考算导写的FFT学习笔记


快速哈特莱变换(FHT)

FHT是一种实序列变换


快速数论变换(NTT)

NTT是在整数域进行的,FFT中通过n次单位福根运算,即满足\(W^n = 1\)\(W\),而NTT,将\(g^{\frac{P-1}{N}}(mod~P)\) 看作与 \(e^{-\frac{2\pi i}{N}}\) 等价,这里\(g\)是模素数\(P\)的原根。综上,数论变换的公式如下:

\[X_k ≡ \sum_{n=0}^{N-1}x_ng^{nk}(mod~P)~~~ k = 0, 1, ...,N-1 \]

逆变换为:

\[x_n ≡ \frac{1}{N}\sum_{k=0}^{N-1}X_kg^{-nk}(mod~P)~~~ n = 0, 1, ...,N-1 \]

Code

#include <cstdio>
#include <algorithm>
typedef long long ll;
constexpr int N = 1 << 18 | 5; 
constexpr int Mod = 998244353;
constexpr int  G = 3;
ll qpow(ll a, ll b) {
    ll ans = 1;
    for(; b; a = (a * a) % Mod, b >>= 1)
        if(b & 1) ans = (ans * a) % Mod;
    return ans;
}
int siz, rev[N];
ll w[N];
inline void init(int n) {
    for(siz = 1; siz < n; siz <<= 1);
    for(int i = 0; i < siz; ++i)
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) * (siz >> 1));
    ll pr = qpow(G, (Mod-1) / siz);
    w[siz / 2] = 1;
    for(int i = 1; i < siz / 2; ++i) w[siz/2+i] = (w[siz/2+i-1]*pr) % Mod;
    for(int i = siz / 2 - 1; i >= 0; --i) w[i] = w[i << 1];
}
inline void DFT(ll A[], int L) {
    static ll t[N];
    int shift = __builtin_ctz(siz) - __builtin_ctz(L);
    for(int i = 0; i != L; ++i) t[rev[i] >> shift] = A[i];
    for(int d = 1; d != L; d <<= 1)
        for(int i = 0; i != L; i += d << 1)
            for(int j = 0; j != d; ++j) {
                ll tmp = t[i + d + j] * w[d + j] % Mod;
                t[i + d + j] = t[i + j] - tmp;
                t[i + j] = t[i + j] + tmp;
                if(t[i + d + j] < 0) t[i + d + j] += Mod;
                if(t[i + j] >= Mod) t[i + j] -= Mod;
            }
    for(int i = 0; i != L; ++i) A[i] = t[i] % Mod;
}
inline void IDFT(ll A[], int L) {
    std::reverse(A + 1, A + L);
    DFT(A, L);
    ll Inv_L = qpow(L, Mod - 2);
    for(int i = 0; i != L; ++i) (A[i] *= Inv_L) %= Mod;
}
int n, m, L;
ll a[N], b[N];
int main() {
	scanf("%d%d",&n,&m);
	for(int i = 0; i <= n; ++i) scanf("%lld", &a[i]);
    for(int i = 0; i <= m; ++i) scanf("%lld", &b[i]);
    for(L = 1; L <= m+n; L <<= 1);  init(L);
    DFT(a, L); DFT(b, L);
    for(int i = 0; i <= L; ++i) a[i] = a[i] * b[i] % Mod;
    IDFT(a, L);
    for(int i = 0; i <= n+m; ++i) printf("%lld ",a[i]); puts("");
    return 0;
}

任意模数FFT

题意:给两个多项式\(f(x)\)\(g(x)\)以及一个模数\(p(p \leq 10^9)\), 求\(f * g (mod ~ p)\)

做法:任意模数NTT,最大的数为\(p^2 max(n,m) \leq 10^{23}\), 所以一般选三个模数即可,求出这三个模数下的答案,然后中国剩余定理。

设当前这一项的系数为\(x\),三个模数分别为\(A, B, C\), 那么:

\[x ≡ x_1 (mod ~ A) \\ x ≡ x_2 (mod ~ B) \\ x ≡ x_3 (mod ~ C) \]

先合并前两项:

\[x_1 + k_1A = x_2 + k_2B = x \\ x_1 + k_1A ≡ x_2 ~(mod ~ B) \\ k_1 ≡ ~\frac {x_2 - x_1} {A}~(mod ~B) \]

及求出\(x ≡ x_1 + k_1A ~(mod ~AB)\), 记作\(x_4 = x_1 + k_1A\)

\[x_4 + k_4AB = x_3 + k_3C \\ x_4 + k_4AB ≡ x_3 ~(mod ~C) \\ k_4 ≡ \frac{x3 - x4}{AB} ~(mod ~C) \]

n那么\(x ≡ x_4 + k_4AB ~(mod~ABC)\),因为\(x < ABC\), 所以\(x = x_4 + k_4AB\)

Code[洛谷P4245]

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
typedef long long ll;
constexpr int N = 1 << 18 | 5;
constexpr ll Mod1 = 469762049;
constexpr ll Mod2 = 998244353;
constexpr ll Mod3 = 1004535809;
constexpr ll M = Mod1 * Mod2;
constexpr int  G = 3;
using namespace std;
inline ll Mul(ll a, ll b, ll Mod) {
    ll ans = 0; ((a %= Mod) += Mod)%= Mod;
    for(; b; a = (a + a) % Mod, b >>= 1)
        if(b & 1) ans = (ans + a) % Mod;
    return ans;
}
inline ll qpow(ll a, ll b, ll Mod) {
    ll ans = 1; ((a %= Mod) += Mod)%= Mod;
    for(; b; a = (a * a) % Mod, b >>= 1)
        if(b & 1) ans = (ans * a) % Mod;
    return ans;
}
int siz, rev[N];
ll w[4][N];
inline void init_w(int typ, ll Mod) {
    ll pr = qpow(G, (Mod-1) / siz, Mod);
    w[typ][siz / 2] = 1;
    for(int i = 1; i < siz / 2; ++i) w[typ][siz/2+i] = (w[typ][siz/2+i-1]*pr) % Mod;
    for(int i = siz / 2 - 1; i >= 0; --i) w[typ][i] = w[typ][i << 1];
}
inline void init(int n) {
    for(siz = 1; siz < n; siz <<= 1);
    for(int i = 0; i < siz; ++i)
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) * (siz >> 1));
    init_w(1,Mod1); init_w(2,Mod2); init_w(3,Mod3);
}
inline void DFT(ll A[], int L, int typ, ll Mod) {
    static ll t[N];
    int shift = __builtin_ctz(siz) - __builtin_ctz(L);
    for(int i = 0; i != L; ++i) t[rev[i] >> shift] = A[i];
    for(int d = 1; d != L; d <<= 1)
        for(int i = 0; i != L; i += d << 1)
            for(int j = 0; j != d; ++j) {
                ll tmp = t[i + d + j] * w[typ][d + j] % Mod;
                t[i + d + j] = t[i + j] - tmp;
                t[i + j] = t[i + j] + tmp;
                if(t[i + d + j] < 0) t[i + d + j] += Mod;
                if(t[i + j] >= Mod) t[i + j] -= Mod;
            }
    for(int i = 0; i != L; ++i) A[i] = t[i] % Mod;
}
inline void IDFT(ll A[], int L, int typ, ll Mod) {
    std::reverse(A + 1, A + L);
    DFT(A, L, typ, Mod);
    ll Inv_L = qpow(L, Mod - 2, Mod);
    for(int i = 0; i != L; ++i) (A[i] *= Inv_L) %= Mod;
}

int n, m, L, P;
ll a[N], b[N], c[N], d[N], ans[3][N];

ll CRT(ll a1, ll a2, ll a3) {
    ll k1 = Mul((a2 - a1),qpow(Mod1, Mod2-2, Mod2), Mod2);
    ll a4 = a1 + k1*Mod1;
    ll k4 = Mul((a3 - a4) , qpow(M, Mod3-2, Mod3), Mod3);
    return (a4%P + Mul(k4 , M, P)) % P;
}

int main() {
	scanf("%d%d%d",&n,&m,&P);
	for(int i = 0; i <= n; ++i) scanf("%lld", &a[i]);
    for(int i = 0; i <= m; ++i) scanf("%lld", &b[i]);
    for(L = 1; L <= m+n; L <<= 1);  init(L);
    
    memcpy(c, a, sizeof(a)); memcpy(d, b, sizeof(a)); DFT(c, L, 1, Mod1); DFT(d, L, 1, Mod1);
    for(int i = 0; i <= L; ++i) ans[0][i] = c[i] * d[i] % Mod1;    
    memcpy(c, a, sizeof(a)); memcpy(d, b, sizeof(a)); DFT(c, L, 2, Mod2); DFT(d, L, 2, Mod2);
    for(int i = 0; i <= L; ++i) ans[1][i] = c[i] * d[i] % Mod2;
    memcpy(c, a, sizeof(a)); memcpy(d, b, sizeof(a)); DFT(c, L, 3, Mod3); DFT(d, L, 3, Mod3);
    for(int i = 0; i <= L; ++i) ans[2][i] = c[i] * d[i] % Mod3;
    IDFT(ans[0], L, 1, Mod1); IDFT(ans[1], L, 2, Mod2); IDFT(ans[2], L, 3, Mod3);
    
    for(int i = 0; i <= n+m; ++i) {
        printf("%lld ",CRT(ans[0][i], ans[1][i], ans[2][i]));
    }puts("");
    return 0;
}

多项式的各种操作

多项式逆元

题意:给定多项式\(A(x)\),请求出一个多项式\(B(x)\),满足\(A(x)*B(x) ≡ 1 (mod~x^n)\)

做法:

  1. \(n = 1\)时,\(A(x) ≡ c (mod~x)\),这样\(A^{-1}(x) ≡ c^{-1}(mod~x)\)
  2. \(n > 1\)时,设\(B(x) ≡ A^{-1}(x)(mod~x)\),则有$$A(x)B(x) ≡ 1 (mod~x^n)$$
    假设在 \(mod~x^{\lceil \frac{n}{2} \rceil}\)意义下,\(A(x)\)的逆元是\(B(x)\)并且我们已经求出,那么有$$A(x)B'(x) ≡ 1 (mod~x^{\lceil \frac{n}{2} \rceil})$$
    显然$$A(x)B(x) ≡ 1 ( mod~x^{\lceil \frac{n}{2} \rceil})$$
    相减可得$$B(x)-B'(x) ≡ 0 (mod~x^{\lceil \frac{n}{2} \rceil})$$
    两边平方得$$B^2(x) - 2B'(x)B(x) + B'^2(x) ≡ 0 (mod~x^n)$$
    两边同乘\(A(x)\),移项可得$$B(x) ≡ 2B'(x) - A(x)B'^2(x) (mod ~x^n)$$

再利用\(FFT\)加速运算即可。

Code[洛谷P4238]

int n, L;
ll a[N], b[N], tmp[N];
void PloyInv(ll A[], ll B[], int n) {
    if(n == 1) {
        B[0] = qpow(A[0], Mod - 2, Mod); return;
    }
    PloyInv(A, B, (n + 1) >> 1);
    int p = 1; while(p < n<<1) p <<= 1;
    copy(A, A+n, tmp); fill(tmp+n, tmp+p, 0);
    DFT(tmp, p); DFT(B, p);
    for(int i = 0; i != p; ++i) {
        B[i] = (2 - tmp[i] * B[i] % Mod) * B[i] % Mod;
        if(B[i] < 0) B[i] += Mod;
    }
    IDFT(B, p);
    fill(B + n, B + p, 0);
}
int main() {
    scanf("%d",&n);
    for(int i = 0; i < n; ++i) scanf("%lld", &a[i]);
    for(L = 2; L <= 2*n-2; L <<= 1); init(L);
    PloyInv(a, b, n);
    for(int i = 0; i < n; ++i) printf("%lld ",b[i]); puts("");
    return 0;
}

多项式除法

题意:给定一\(n\)次多项式\(A(x)\)和一个\(m(m \leq n)\)次多项式\(B(x)\),要求求出\(D(x), R(x)\)满足$$A(x) = D(x)B(x) + R(x)$$且\(degD \leq degA - degB = n-m, ~degR < m\)

做法:首先,想办法消除\(R(x)\)的影响。首先我定义一个运算

\[A^R(x)= x^n A( \frac{1}{x} ) \]

可以发现,这个操作在进行系数反转。

现在将原式的 \(x\) 全部替换为 \(\frac{1}{x}\) ,然后两边同乘\(x^n\),可得

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

观察这个式子,\(D(x)\)反转后,次数不会高于\(n-m\),而\(x^{n-m+1}R^R(x)\)这个部分最低次高于\(n-m\),把上式放到\(mod ~x^{n-m+1}\)意义下,\(R(x)\)就会被消除,现在式子变为$$A^R(x) ≡ D{R}(x)BR(x) (mod ~x^{n-m+1})$$这样只需要求\(B^R(x)\)的逆元即可,\(D(x)\)可以通过回带\(R(x)\)求得。

void PloyDiv(ll A[], ll B[], ll D[], ll R[], int n, int m) {
    static ll A0[N], B0[N];
    int p = 1, t = n - m + 1;
    while(p < t<<1) p <<= 1;

    fill(A0, A0 + p, 0);
    reverse_copy(B, B+m, A0);
    PloyInv(A0, B0, t);
    fill(B0+t, B0+p, 0);
    DFT(B0, p);
    reverse_copy(A, A+n, A0);
    fill(A0 + t, A0 + p, 0);
    DFT(A0, p);
    for(int i = 0; i != p; ++i)
        A0[i] = Mul(A0[i] , B0[i]);
    IDFT(A0, p);
    reverse(A0, A0+t); copy(A0, A0+t,D);

    p = 1; while(p < n) p <<= 1;
    fill(A0 + t, A0 + p, 0);
    DFT(A0, p);
    copy(B, B+m, B0);
    fill(B0 + m, B0 + p, 0);
    DFT(B0, p);
    for(int i = 0; i != p; ++i) A0[i] = Mul(A0[i] , B0[i]);
    IDFT(A0, p);
    for(int i = 0; i != m; ++i) R[i] = Dec(A[i] , A0[i]);
    fill(R+m, R+p, 0);
}

int main() {
    scanf("%d%d",&n,&m); ++n; ++m;
    for(int i = 0; i < n; ++i) scanf("%lld", &a[i]);
    for(int i = 0; i < m; ++i) scanf("%lld", &b[i]);
    int LL = max(max(n, m), (n - m + 1) << 1);
    L = 1; while(L < LL) L <<= 1; init(L);
    
    PloyDiv(a, b, D, R, n, m);
    
    for(int i = 0; i < n-m+1; ++i) printf("%lld ",D[i]); puts("");
    for(int i = 0; i < m-1; ++i) printf("%lld ",R[i]); puts("");
    return 0;
}

多项式牛顿迭代

题意:已知函数\(G(z)\),求一个多项式 \(F(z) ~mod ~z^n\) ,满足$$G(F(z)) ≡ 0 ~(mod ~z^n)$$

做法:类似多项式求逆的做法。
当n = 1时,\(G(F(z)) ≡ 0 (mod~ z)\)要单独求出。现在假设已经求出$$G(F_0(z)) ≡ 0(modz^{\lceil \frac{n}{2} \rceil})$$考虑如何将答案,扩展至 \(mod~z^n\)下,把\(G(F(z))\)\(F_0(z)\)泰勒展开$$G(F(z))=G(F_0(z))+\frac{G'(F_0(z))}{1!}(F(z)-F_0(z)) + \frac{G''(F_0(z))}{2!}(F(z)-F_0(z))^2 + ...$$因为\(F(z)\)\(F_0(z)\)的最后 \(\lceil \frac{n}{2}\rceil\) 项相同,所以 \((F(z)-F_0(z))^2\) 的最低非0项次数大于 \(2\lceil \frac{n}{2}\rceil\) ,所以有$$G(F(z))≡G(F_0(z))+\frac{G'(F_0(z))}{1!}(F(z)-F_0(z)) (modz^n)$$因为\(G(F(z)) ≡ 0 (mod z^n)\), 那么有$$F(z) ≡F_0(z) - \frac{G(F_0(z))}{G'(F_0(z))} ~(mod ~z^n)$$


多项式开方

题意:给定一个 \(n-1\) 次多项式 \(A(x)\),求一个在 \(mod~x^n\) 意义下的多项式 \(B(x)\),满足\(B^2(x) ≡ A(x)~(mod~x^n)\)

做法:移向可得\(B^2(x) - A(x) ≡ 0 ~(mod~x^n)\) ,设 \(G(B(x)) = B^2(x) - A(x)\), 有\(G'(B(x)) = 2B(x)\),带入牛顿迭代的式子里,则有:

\[B(x) ≡ B_0(x) - \frac{B_0^2(x)-A(x)}{2B_0(x)} ≡ \frac{B_0^2(x)+A(x)}{2B_0(x)}~(mod~x^n) \]

注意到当 \(n = 1\) 时,如果题目不保证 \(a_0 = 1\),可能需要计算二次剩余

Code[洛谷P5205]

void PloySqrt(ll A[], ll B[], int n) {
    static ll T[N];
    if(n == 1) {
        B[0] = 1; return;
    }
    int p = 1, hf =  (n+1) >> 1; while(p < n<<1) p <<= 1;
    PloySqrt(A, B, hf); fill(B + hf, B + p, 0);
    PloyInv(B, T, n); fill(T + n, T + p, 0);
    DFT(T, p);

    fill(B + hf, B + p, 0);
    DFT(B, p >> 1);
    for(int i = 0; i != (p >> 1); ++i)
        B[i] = (B[i]*B[i]) % Mod;
    IDFT(B, p >> 1);
    for(int i = 0; i != n; ++i)
        B[i] = (A[i] + B[i]) % Mod * Inv2 % Mod;
    DFT(B, p);
    for(int i = 0; i != p; ++i)
        B[i] = (B[i] * T[i]) % Mod;
    IDFT(B, p);
}
int main() {
    scanf("%d",&n);
    for(int i = 0; i < n; ++i) scanf("%lld", &a[i]);
    for(L = 2; L <= n << 1; L <<= 1); init(L);
    Inv2 = qpow(2,Mod-2,Mod);
    PloySqrt(a, b, n);
    for(int i = 0; i < n; ++i) printf("%lld ",b[i]); puts("");
    return 0;
}

多项式 ln 和多项式 exp


多项式ln

  • 定义:对于多项式 \(A(x) = \sum_{i \geq 1}a_ix^i\) 多项式ln有$$ln(1-A(x)) = -\sum_{i \geq 1}\frac{A^i(x)}{i}$$

  • 计算
    现在有\(A(x) = 1 + \sum_{i \geq 1} a_ix^i\)
    \(ln(A(x))\) 求导有$$(lnA(x))' = \frac{A'(x)}{A(x)}$$ 只需计算 \(A(x)\) 的逆元,再完成求导和积分运算即可

Code[洛谷4725]

void PloyD(ll A[],ll B[], int n) {
    copy(A, A+n, B);
    for(int i = 0; i < n-1; ++i) {
        B[i] = 1ll*B[i+1]*(i+1) % Mod;
    } B[n-1] = 0;
}
void PloyF(ll A[], ll B[], int n) {
    copy(A, A+n, B);
    for(int i = n-1; i; --i) {
        B[i] = B[i-1] * Inv[i] % Mod;
    } B[0] = 0;
}
void PloyLn(ll A[], ll B[], int n) {
    static ll T[N];
    int p = 1; while(p < n << 1) p <<= 1;
    PloyInv(A, T, n); fill(T+n, T+p, 0); DFT(T, p);
    PloyD(A,B,n); fill(B+n, B+p, 0); DFT(B, p);
    for(int i = 0; i != p; ++i) T[i] = (B[i] * T[i]) % Mod;
    IDFT(T, p);
    PloyF(T, B, n); fill(B+n, B+p, 0);
}
int main() {
    scanf("%d",&n);
    for(int i = 0; i < n; ++i) scanf("%lld", &a[i]);
    for(L = 2; L <= n << 1; L <<= 1); init(L);
    Inv[1] = 1;
    for(ll i = 2; i <= L; i++)
        Inv[i] = (Mod - Mod / i) % Mod * Inv[Mod % i] % Mod;
    PloyLn(a, b, n);
    for(int i = 0; i < n; ++i) printf("%lld ",b[i]); puts("");
    return 0;
}

多项式exp

  • 定义:对于多项式 \(A(x) = \sum_{i \geq 1}a_ix^i\) 多项式exp有$$e^{A(x)} = \sum_{i \geq 0}\frac{A^i(x)}{i!}$$

  • 计算

\(F(x) = e^{A(x)}\) ,变形后得到 $$ln ~F(x) - A(x) = 0$$ 构造函数 $$G(F(x)) = ln~F(x) - A(x) $$, \(G'(F(x)) = \frac{1}{F(x)}\),代入牛顿迭代$$F(x) ≡ F_0(x)(1 - ln~F_0(x) + A(x)) (modx^n)$$ 当 \(n = 1\)\(F(x) = 1\)

Code[洛谷P4726]

void Ployexp(ll A[], ll B[], int n) {
    static ll T[N];
    if(n == 1) {
        B[0] = 1; return;
    }
    int p = 1, hf = (n + 1) >> 1; while(p < n<<1) p <<= 1;
    Ployexp(A, B, hf); fill(B + hf, B + p, 0);
    PloyLn(B, T, n);
    for(int i = 0; i != n; ++i) T[i] = (A[i] - T[i] + Mod) % Mod;
    ++ T[0]; if(T[0] >= Mod) T[0] -= Mod;
    DFT(T, p); DFT(B, p);
    for(int i = 0; i != p; ++i) B[i] = B[i] * T[i] % Mod;
    IDFT(B, p);
}
int main() {
    scanf("%d",&n);
    for(int i = 0; i < n; ++i) scanf("%lld", &a[i]);
    for(L = 2; L <= n << 1; L <<= 1); init(L);
    Inv[1] = 1;
    for(ll i = 2; i <= L; i++)
        Inv[i] = (Mod - Mod / i) % Mod * Inv[Mod % i] % Mod;
    Ployexp(a, b, n);
    for(int i = 0; i < n; ++i) printf("%lld ",b[i]); puts("");
    return 0;
}

多项式任意次幂

题意:给出多项式\(A(x)\),求\(A^k(x),k \in Q\)

做法:

  1. k为整数时可以快速幂
  2. 可以利用\(A^k(x) = e^{k~ln~A(x)}\) 计算

多项式三角函数

题意:给出多项式\(A(x)\),求\(sin A(x)~(mod~x^n)\), \(cos A(x)~(mod~x^n)\)

做法:利用欧拉公式:$$e^{iA(x)} = cosA(x) + i sinA(x)$$与常规的多项式exp基本一致,只能用FFT,运算全部为复数,注意边界


多项式多点求值


多项式快速插值


多项式复合逆


多项式相关题目及应用

分治FFT

题意:给定长度为\(n-1\)的序列\(g[1],...,g[n-1]\)计算\(f[0],...,f[n-1]\)

\[f[i] = \sum_{j=1}^i f[i-j]g[j] = \sum_{j=0}^{i-1} f[j]g[i-j] \]

\(f[0] = 1\)

做法:CDQ+FFT。考虑已经求出\([l,mid]\)\(f[i]\),计算他们堆\([mid+1,r]\)的贡献$w[x], x \in [mid+1,r] $ 有

\[ w[x] = \sum_{i=l}^{mid} f[i]g[x-i] \rightarrow w[x] = \sum_{i=l}^{x-1} f[i]g[x-i] \]

(设\([mid+1,r]\)\(f[i]=0\)
\(k = i-l\),

\[w[x] = \sum_{k=0}^{x-l-1} f[k+l]g[x-k-l] = \sum_{k=0}^{x-l-1} a[k]b[x-l-1-k] \\ \]

我们凑出对应的\(a[i], b[i]\)

\[a[k] = f[k+l]\\ b[x-l-1-k] = g[x-l-k] \rightarrow \\ b[x-l-1+k] = g[x-l+k] \rightarrow \\ b[k-1] = g[k] \rightarrow b[k] = g[k+1] \]

Code[洛谷P4721 分治FFT]

int n, L;
ll Ans[N], g[N], a[N], b[N];
void solve(int l, int r) {
    if(l == r) return;
    int mid = (l + r) >> 1;
    solve(l,mid);
    int len = r - l + 1, p = 1;
    for(;p < len; p <<= 1); // 我们最多需要r-l+1前项!!!
    for(int i = 0; i < p; ++ i) a[i] = b[i] = 0;
    for(int i = l; i <= mid; ++ i) a[i-l] = Ans[i];
    for(int i = 0; i <= r-l; ++ i) b[i] = g[i+1];
    DFT(a,p), DFT(b,p);
    for(int i = 0; i < p; ++i) a[i] = a[i]*b[i]%Mod;
    IDFT(a,p);
    rep(i,mid+1,r) {
        Ans[i] += a[i-l-1]; 
        if(Ans[i] >= Mod) Ans[i] -= Mod;
    }
    solve(mid+1,r);
}
int main() {
    read(n); rep(i,1,n-1) read(g[i]);
    for(L = 2; L <= n << 1; L <<= 1); init(L);
    Ans[0] = 1;
    solve(0,n-1);
    rep(i,0,n-1) write(Ans[i]), putchar(' '); putchar('\n');
}

参考资料

  1. DFT与圆周卷积
  2. 任意模数FFT
  3. 从DTFT到DFS,从DFS到DFT,从DFT到FFT,从一维到二维
  4. 一位神犇的Blog
  5. 另一位神犇的Blog
posted @ 2019-01-31 17:22  RRRR_wys  阅读(362)  评论(0编辑  收藏  举报