[算法学习] 多项式全家桶 - 2

多项式求逆

题目链接:洛谷P4238 多项式乘法逆

Description

给定一个多项式 \(F(x)\) ,请求出一个多项式 \(G(x)\) ,满足 \(F(x)*G(x) \equiv 1 (mod\ x^n)\)。系数对 \(998244353\) 取模。
数据范围 \(1\le n\le 10^5, 0\le a_i \le 10^9\)
时间限制 \(1\ s\)

Solution

求模 \(x^n\) 的逆元为 \(B\),模 \(x^{\frac{n}{2}}\) 逆元为 \(C\) , 则
\(\begin{cases} A*C\equiv 1 (mod\ x^{\frac{n}{2}}) \\ A*B\equiv 1 (mod\ x^{\frac{n}{2}}) \end{cases}\)
\(C-B\equiv 0 (mod\ x^{\frac{n}{2}})\)
两边平方,则\((C-B)^2 \equiv 0 (mod\ x^n)\)
\(C^2 - 2CB + B^2 \equiv 0 (mod\ x^n)\)
\(AC^2-2C+B \equiv 0 (mod\ x^n)\)
\(B \equiv 2C-AC^2 (mod\ x^n)\)
于是,我们可以递归求解,在求出\(C\)后,可将其转移到\(B\)上。
复杂度\(T(n)=T(\frac{n}{2})+nlogn\),由主定理可知复杂度为\(O(nlogn)\)

Code

const int N = 400005;
const int mod = 998244353;
const int G = 3;
const int Gi = 332748118;
int qpow(int a, int b) {
  int res = 1;
  while (b > 0) {
    if (b & 1) res = 1ll * res * a % mod;
    a = 1ll * a * a % mod;
    b >>= 1;
  }
  return res;
}

int a[N], b[N], c[N], n;
namespace Poly {
  int r[N], lim = 1, L = 0;
  void getR(int n) {
    lim = 1, L = 0;
    while (lim <= n) lim <<= 1, L++;
    for (rint i = 0; i < lim; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << L - 1);
  }
  void NTT(int *a, int opt) {
    for (rint i = 0; i < lim; i++) if (i < r[i]) swap(a[i], a[r[i]]);
    for (rint mid = 1; mid < lim; mid <<= 1) {
      int Wn = qpow(opt == 1 ? G : Gi, (mod - 1) / (mid << 1));
      for (rint R = mid << 1, j = 0; j < lim; j += R) {
        int w = 1;
        for (rint k = 0; k < mid; k++, w = 1ll * w * Wn % mod) {
          int x = a[j + k], y = 1ll * w * a[j + k + mid] % mod;
          a[j + k] = (x + y) % mod;
          a[j + k + mid] = (x - y + mod) % mod;
        }
      }
    }
    if (opt == -1) {
      int linv = qpow(lim, mod - 2);
      for (rint i = 0; i < lim; i++) a[i] = 1ll * a[i] * linv % mod;
    }
  }
  void mul(int *a, int *b, int n, int m) {
    getR(n + m);
    NTT(a, 1), NTT(b, 1);
    for (rint i = 0; i < lim; i++) a[i] = 1ll * a[i] * b[i] % mod;
    NTT(a, -1);
  }
  void inv(int *a, int *b, int n) {
    if (n == 1) { b[0] = qpow(a[0], mod - 2); return ; }
    inv(a, b, (n + 1) >> 1);
    getR(n + n);
    for (rint i = 0; i < n; i++) c[i] = a[i];
    for (rint i = n; i < lim; i++) c[i] = 0;
    NTT(c, 1), NTT(b, 1);
    for (rint i = 0; i < lim; i++) {
      b[i] = (2ll - 1ll * c[i] * b[i] % mod + mod) % mod * b[i] % mod;
    }
    NTT(b, -1);
    for (rint i = n; i < lim; i++) b[i] = 0;
  }
}

int main() {
  n = read();
  for (rint i = 0; i < n; i++) a[i] = read();
  Poly::inv(a, b, n);
  for (rint i = 0; i < n; i++) print(b[i]), putchar(' ');
  return 0;
}

多项式开根

题目链接:洛谷P5205 多项式开根

前置技能

多项式求逆

Description

给定一个多项式 \(A(x)\) ,求多项式 \(B(x)\) 使得 \(B^2(x)\equiv A(x) (mod\ x^n)\)
若有多解,取零次项系数较小的作为答案,数据保证\(a_0=1\)

Solution

求模 \(x^n\) 的逆元为 \(B\),模 \(x^{\frac{n}{2}}\) 逆元为 \(C\) , 则
\(\begin{cases} C^2\equiv A (mod\ x^{\frac{n}{2}}) \\ B^2\equiv A (mod\ x^{\frac{n}{2}}) \end{cases}\)
\((B+C)(B-C)\equiv 0 (mod\ x^{\frac{n}{2}})\)

有两组解,我们取\(B-C\)这组。
\(B-C\equiv 0 (mod\ x^{\frac{n}{2}})\)
\(A-2BC+C^2\equiv 0 (mod\ x^n)\)
\(B\equiv \frac{A+C^2}{2C} (mod\ x^n)\)
递归求解即可,注意边界\(a_0=1\),所以在\(n=1\)\(b_0=1\)即可。
复杂度 \(O(nlogn)\)

Code

const int N = 400005;
const int mod = 998244353;
const int G = 3;
const int Gi = 332748118;
int qpow(int a, int b) {
  int res = 1;
  while (b > 0) {
    if (b & 1) res = 1ll * res * a % mod;
    a = 1ll * a * a % mod;
    b >>= 1;
  }
  return res;
}

int a[N], b[N], c[N], d[N], f[N], n;
namespace Poly {
  int r[N], lim = 1, L = 0;
  void getR(int n) {
    lim = 1, L = 0;
    while (lim <= n) lim <<= 1, L++;
    for (rint i = 0; i < lim; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << L - 1);
  }
  void NTT(int *a, int opt) {
    for (rint i = 0; i < lim; i++) if (i < r[i]) swap(a[i], a[r[i]]);
    for (rint mid = 1; mid < lim; mid <<= 1) {
      int Wn = qpow(opt == 1 ? G : Gi, (mod - 1) / (mid << 1));
      for (rint R = mid << 1, j = 0; j < lim; j += R) {
        int w = 1;
        for (rint k = 0; k < mid; k++, w = 1ll * w * Wn % mod) {
          int x = a[j + k], y = 1ll * w * a[j + k + mid] % mod;
          a[j + k] = (x + y) % mod;
          a[j + k + mid] = (x - y + mod) % mod;
        }
      }
    }
    if (opt == -1) {
      int linv = qpow(lim, mod - 2);
      for (rint i = 0; i < lim; i++) a[i] = 1ll * a[i] * linv % mod;
    }
  }
  void mul(int *a, int *b, int n, int m) {
    getR(n + m);
    NTT(a, 1), NTT(b, 1);
    for (rint i = 0; i < lim; i++) a[i] = 1ll * a[i] * b[i] % mod;
    NTT(a, -1);
  }
  void inv(int *a, int *b, int n) {
    if (n == 1) { b[0] = qpow(a[0], mod - 2); return ; }
    inv(a, b, (n + 1) >> 1);
    getR(n + n);
    for (rint i = 0; i < n; i++) c[i] = a[i];
    for (rint i = n; i < lim; i++) c[i] = 0;
    NTT(c, 1), NTT(b, 1);
    for (rint i = 0; i < lim; i++) {
      b[i] = (2ll - 1ll * c[i] * b[i] % mod + mod) % mod * b[i] % mod;
    }
    NTT(b, -1);
    for (rint i = n; i < lim; i++) b[i] = 0;
  }
  void Sqrt(int *a, int *b, int n) {
    if (n == 1) { b[0] = sqrt(a[0]); return ; }
    Sqrt(a, b, (n + 1) >> 1);
    getR(n + n);
    for (rint i = 0; i < n; i++) d[i] = 1ll * 2 * b[i] % mod;
    for (rint i = 0; i < lim; i++) f[i] = 0;
    inv(d, f, n);
    NTT(b, 1);
    for (rint i = 0; i < lim; i++) b[i] = 1ll * b[i] * b[i] % mod;
    NTT(b, -1);
    for (rint i = 0; i < n; i++) b[i] = (b[i] + a[i]) % mod;
    NTT(b, 1), NTT(f, 1);
    for (rint i = 0; i < lim; i++) b[i] = 1ll * b[i] * f[i] % mod;
    NTT(b, -1);
    for (rint i = n; i < lim; i++) b[i] = 0;
  }
}

int main() {
  n = read();
  for (rint i = 0; i < n; i++) a[i] = read();
  Poly::Sqrt(a, b, n);
  for (rint i = 0; i < n; i++) print(b[i]), putchar(' ');
  return 0;
}

多项式对数函数(ln)

题目链接:洛谷P4725 多项式对数函数(多项式 ln)

前置技能

多项式求逆,求导,积分

Description

给定一个多项式 \(A(x)\) ,求多项式 \(B(x) \equiv ln(A(x)) (mod\ x^n)\)
在模数为\(998244353\)下进行,保证 \(a_0=1\),且 \(a_i \in [0, 998244353) ∩ Z\)
数据范围 \(1\le n\le 10^5\)

Solution

\(B \equiv ln(A) (mod\ x^n)\)
两边同时求导:\(B' \equiv \frac{A'}{A} (mod\ x^n)\)
那么,我们可以先计算出\(A'\),然后将\(A\)求逆,跑一次卷积,然后将\(B'\)积分回去即可。
复杂度 \(O(nlogn)\)

Code

const int N = 400005;
const int mod = 998244353;
const int G = 3;
const int Gi = 332748118;
int qpow(int a, int b) {
  int res = 1;
  while (b > 0) {
    if (b & 1) res = 1ll * res * a % mod;
    a = 1ll * a * a % mod;
    b >>= 1;
  }
  return res;
}

int a[N], b[N], c[N], d[N], aa[N], n;
namespace Poly {
  int r[N], lim = 1, L = 0;
  void getR(int n) {
    lim = 1, L = 0;
    while (lim <= n) lim <<= 1, L++;
    for (rint i = 0; i < lim; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << L - 1);
  }
  void NTT(int *a, int opt) {
    for (rint i = 0; i < lim; i++) if (i < r[i]) swap(a[i], a[r[i]]);
    for (rint mid = 1; mid < lim; mid <<= 1) {
      int Wn = qpow(opt == 1 ? G : Gi, (mod - 1) / (mid << 1));
      for (rint R = mid << 1, j = 0; j < lim; j += R) {
        int w = 1;
        for (rint k = 0; k < mid; k++, w = 1ll * w * Wn % mod) {
          int x = a[j + k], y = 1ll * w * a[j + k + mid] % mod;
          a[j + k] = (x + y) % mod;
          a[j + k + mid] = (x - y + mod) % mod;
        }
      }
    }
    if (opt == -1) {
      int linv = qpow(lim, mod - 2);
      for (rint i = 0; i < lim; i++) a[i] = 1ll * a[i] * linv % mod;
    }
  }
  void mul(int *a, int *b, int n, int m) {
    getR(n + m);
    NTT(a, 1), NTT(b, 1);
    for (rint i = 0; i < lim; i++) a[i] = 1ll * a[i] * b[i] % mod;
    NTT(a, -1);
  }
  void inv(int *a, int *b, int n) {
    if (n == 1) { b[0] = qpow(a[0], mod - 2); return ; }
    inv(a, b, (n + 1) >> 1);
    getR(n + n);
    for (rint i = 0; i < n; i++) c[i] = a[i];
    for (rint i = n; i < lim; i++) c[i] = 0;
    NTT(c, 1), NTT(b, 1);
    for (rint i = 0; i < lim; i++) {
      b[i] = (2ll - 1ll * c[i] * b[i] % mod + mod) % mod * b[i] % mod;
    }
    NTT(b, -1);
    for (rint i = n; i < lim; i++) b[i] = 0;
  }
  void Dao(int *a, int n) {
    for (rint i = 0; i < n - 1; i++) {
      a[i] = 1ll * a[i + 1] * (i + 1) % mod;
    }
    a[n - 1] = 0;
  }
  void Jifen(int *a, int n) {
    for (rint i = n - 1; i > 0; i--) {
      a[i] = 1ll * a[i - 1] * qpow(i, mod - 2) % mod;
    }
    a[0] = 0;
  }
  void Ln(int *a, int *b, int n) {
    for (rint i = 0; i < n; i++) aa[i] = a[i];
    Dao(aa, n), inv(a, d, n);
    mul(aa, d, n, n);
    Jifen(aa, n);
    for (rint i = 0; i < n; i++) b[i] = aa[i];
  }
}

int main() {
  n = read();
  for (rint i = 0; i < n; i++) a[i] = read();
  Poly::Ln(a, b, n);
  for (rint i = 0; i < n; i++) print(b[i]), putchar(' ');
  return 0;
}

多项式指数函数(exp)

题目链接:洛谷P4726 多项式指数函数(多项式 exp)

Description

给定一个多项式 \(A(x)\) ,求一个多项式 \(B(x)\) 满足 \(B(x) \equiv e^{A(x)} (mod\ x^n)\)
系数对 \(998244353\) 取模。
数据范围 \(1\le n\le 100000\),保证 \(a_0=1\)

Solution

rt

Code

posted @ 2020-04-08 09:15  wlzhouzhuan  阅读(174)  评论(0)    收藏  举报