20200615 专题:多项式全家桶

多项式卷积

FFT

(原理先咕着)
精度炸裂

#include <bits/stdc++.h>
using namespace std;
#define in Read()

inline int in {
  int s = 0, f = 1;
  char x;
  for (x = getchar(); x < '0' || x > '9'; x = getchar())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}

const int A = 5e6 + 5;
const double pi = acos(-1);
int n, m;
int las, num;
int r[A];
struct node {
  double x, y;
  node(double u = 0, double v = 0) { x = u, y = v; }
  inline friend node operator+(const node u, const node v) {
    return node(u.x + v.x, u.y + v.y);
  }
  inline friend node operator-(const node u, const node v) {
    return node(u.x - v.x, u.y - v.y);
  }
  inline friend node operator*(const node u, const node v) {
    return node(u.x * v.x - u.y * v.y, u.x * v.y + u.y * v.x);
  }
} a[A], b[A];

inline void fft(node *Q, double inv) {
  for (int i = 0; i < las; i++)
    if (i < r[i]) swap(Q[i], Q[r[i]]);
  for (int mid = 1; mid < las; mid <<= 1) {
    node wn = node(cos(pi / mid), sin(pi / mid) * inv);
    for (int i = 0; i < las; i += mid << 1) {
      node fw = node(1, 0);
      for (int j = 0; j < mid; j++, fw = fw * wn) {
        node u = Q[i + j], v = Q[i + j + mid] * fw;
        Q[i + j] = u + v;
        Q[i + j + mid] = u - v;
      }
    }
  }
  return;
}

inline void fold() {
  las = 1, num = 0;
  while (las <= n + m) las <<= 1, num++;
  for (int i = 0; i < las; i++)
    r[i] = (r[i >> 1] >> 1) | ((i & 1) << (num - 1));
  fft(a, 1), fft(b, 1);
  for (int i = 0; i < las; i++) a[i] = a[i] * b[i];
  fft(a, -1);
  for (int i = 0; i <= n + m; i++) a[i].x = (int)(a[i].x / las + 0.5);
  return;
}

signed main() {
  n = in, m = in;
  for (int i = 0; i <= n; i++) a[i].x = in;
  for (int i = 0; i <= m; i++) b[i].x = in;
  fold();
  for (int i = 0; i <= n + m; i++) printf("%d ", (int)a[i].x);
  return 0;
}
NTT

fft 中的复平面单位根换成了原根
质数:998244353 原根:3

#include <bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long

inline int in {
  int s = 0, f = 1;
  char x;
  for (x = getchar(); x < '0' || x > '9'; x = getchar())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}

const int A = 5e6 + 5;
const int mod = 998244353;
int n, m, r[A];
int a[A], b[A];
int lim;

inline int quick(int s, int c) {
  int ans = 1;
  while (c) {
    if (c & 1) ans = ans * s % mod;
    s = s * s % mod;
    c >>= 1;
  }
  return ans;
}

inline void ntt(int *Q, int pos) {
  for (int i = 0; i < lim; i++)
    if (i < r[i]) swap(Q[i], Q[r[i]]);
  for (int mid = 1; mid < lim; mid <<= 1) {
    int wn = quick(3, (mod - 1) / (mid << 1));
    for (int i = 0; i < lim; i += (mid << 1)) {
      int fw = 1;
      for (int j = 0; j < mid; j++, fw = fw * wn % mod) {
        int x = Q[i + j], y = Q[i + j + mid] * fw % mod;
        Q[i + j] = (x + y) % mod;
        Q[i + j + mid] = (x - y + mod) % mod;
      }
    }
  }
  if (pos) return;
  int inv = quick(lim, mod - 2);
  reverse(Q + 1, Q + lim);
  for (int i = 0; i < lim; i++) Q[i] = Q[i] * inv % mod;
  return;
}

inline void fold(int *x, int *y, int la, int lb) {
  lim = 1;
  while (lim < la + lb) lim <<= 1;
  for (int i = 0; i < lim; i++)
    r[i] = (r[i >> 1] >> 1) | ((i & 1) ? lim >> 1 : 0);
  ntt(a, 1), ntt(b, 1);
  for (int i = 0; i < lim; i++) a[i] = a[i] * b[i] % mod;
  ntt(a, 0);
  return;
}

signed main() {
  n = in, m = in;
  n++, m++;
  for (int i = 0; i < n; i++) a[i] = in;
  for (int i = 0; i < m; i++) b[i] = in;
  fold(a, b, n, m);
  for (int i = 0; i < n + m - 1; i++) printf("%lld ", a[i]);
  return 0;
}
分治NTT

cdq 分治的思路处理

#include <bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long

inline int in {
  int s = 0, f = 1;
  char x;
  for (x = getchar(); x < '0' || x > '9'; x = getchar())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}

const int A = 4e5 + 5;
const int mod = 998244353;
int lim, r[A];

inline int power(int s, int c) {
  int ans = 1;
  while (c) {
    if (c & 1) ans = ans * s % mod;
    s = s * s % mod;
    c >>= 1;
  }
  return ans;
}

inline void init(int len) {
  lim = 1;
  while (lim < len) lim <<= 1;
  for (int i = 1; i < lim; i++)
    r[i] = (r[i >> 1] >> 1) | ((i & 1) ? lim >> 1 : 0);
  return;
}

inline void ntt(int *Q, int pos) {
  for (int i = 0; i < lim; i++)
    if (i < r[i]) swap(Q[i], Q[r[i]]);
  for (int mid = 1; mid < lim; mid <<= 1) {
    int wn = power(3, (mod - 1) / (mid << 1));
    for (int i = 0; i < lim; i += (mid << 1)) {
      int fw = 1;
      for (int j = 0; j < mid; j++, fw = fw * wn % mod) {
        int x = Q[i + j], y = Q[i + j + mid] * fw % mod;
        Q[i + j] = (x + y) % mod;
        Q[i + j + mid] = (x - y + mod) % mod;
      }
    }
  }
  if (pos) return;
  int k = power(lim, mod - 2);
  reverse(Q + 1, Q + lim);
  for (int i = 0; i < lim; i++) Q[i] = Q[i] * k % mod;
  return;
}

inline void Flow(int *x, int *y, int lx, int ly) {
  init(lx + ly);
  ntt(x, 1), ntt(y, 1);
  for (int i = 0; i < lim; i++) x[i] = x[i] * y[i] % mod;
  ntt(x, 0);
  return;
}

int n;
int a[A], b[A];

int p1_ntt[A], p2_ntt[A];
inline void solve(int *x, int *y, int l, int r) {
#define p1 p1_ntt
#define p2 p2_ntt
  if (l == r) {
    if (!l) y[0] = 1;
    return;
  }
  int mid = (l + r) >> 1;
  solve(x, y, l, mid);
  for (int i = 0; i < lim; i++) p1[i] = p2[i] = 0;
  for (int i = l; i <= mid; i++) p1[i - l] = y[i];
  for (int i = 0; i <= r - l; i++) p2[i] = x[i];
  Flow(p1, p2, r - l + 1, 0);
  for (int i = mid + 1; i <= r; i++) y[i] = (y[i] + p1[i - l]) % mod;
  solve(x, y, mid + 1, r);
#undef p1
#undef p2
}

signed main() {
  n = in;
  for (int i = 1; i < n; i++) a[i] = in;
  solve(a, b, 0, n - 1);
  for (int i = 0; i < n; i++) printf("%lld ", b[i]);
  puts("");
  return 0;
}

多项式求逆

A × B ≡ 1 ( m o d x n ) A\times B \equiv 1(mod x^n) A×B1(modxn)
AB
有 A × B ≡ 1 ( m o d   x n ) 假 设 已 知 B ′ A × B ′ ≡ 1 ( m o d   x n 2 ) B − B ′ ≡ 0 ( m o d   x n 2 ) ( B − B ′ ) 2 ≡ 0 ( m o d   x n ) B 2 − 2 B B ′ + B ′ 2 ≡ 0 ( m o d   x n ) A ( B 2 − 2 B B ′ + B ′ 2 ) ≡ 0 ( m o d   x n ) 因 为 A × B ≡ 1 ( m o d   x n ) 有 B − 2 B ′ + A B ′ 2 ≡ 0 ( m o d   x n ) B − 2 B ′ + A B ′ 2 ≡ 0 ( m o d   x n ) B ≡ 2 B ′ − A B ′ 2 ( m o d   x n ) B ≡ B ′ ( 2 − A B ′ ) ( m o d   x n ) 有A\times B \equiv 1(mod\ x^n)\\ 假设已知 B'\\ A\times B' \equiv 1(mod\ x^\frac{n}{2})\\ B-B' \equiv 0(mod\ x^\frac{n}{2})\\ (B-B')^2 \equiv 0(mod\ x^n)\\ B^2-2BB'+B'^2\equiv 0(mod\ x^n)\\ A(B^2-2BB'+B'^2)\equiv 0(mod\ x^n)\\ 因为A\times B \equiv 1(mod\ x^n)\\ 有B-2B'+AB'^2\equiv 0(mod\ x^n)\\ B-2B'+AB'^2\equiv 0(mod\ x^n)\\ B\equiv 2B'-AB'^2(mod\ x^n)\\ B\equiv B'(2-AB')(mod\ x^n) A×B1(mod xn)BA×B1(mod x2n)BB0(mod x2n)(BB)20(mod xn)B22BB+B20(mod xn)A(B22BB+B2)0(mod xn)A×B1(mod xn)B2B+AB20(mod xn)B2B+AB20(mod xn)B2BAB2(mod xn)BB(2AB)(mod xn)
建议背诵全文

多项式ln

B ≡ l n A ( m o d   x n ) B\equiv lnA(mod\ x^n) BlnA(mod xn)
AB
B ≡ l n A ( m o d   x n ) 同 时 求 导 有 B ′ ≡ ( l n A ) ′ ( m o d   x n ) ( l n A ) ′ 为 复 合 函 数 求 导 , 链 式 法 则 展 开 有 B ′ ≡ l n ′ A × A ′ ( m o d   x n ) B ′ ≡ A ′ A ( m o d   x n ) B\equiv lnA(mod\ x^n)\\ 同时求导有\\ B'\equiv (lnA)'(mod\ x^n)\\ (lnA)'为复合函数求导,链式法则展开有\\ B'\equiv ln'A\times A'(mod\ x^n)\\ B'\equiv \frac{A'}{A}(mod\ x^n)\\ BlnA(mod xn)B(lnA)(mod xn)(lnA)BlnA×A(mod xn)BAA(mod xn)
然后求导,求逆,卷积,积分即可
求 导 : ( a i x i ) ′ = i a i x i − 1 积 分 : ∫ a i x i d x = a i 1 i + 1 x i + 1 求导:(a_i x^i)'=i a_i x^{i-1}\\ 积分:\int a_i x^i dx=a_i \frac{1}{i+1} x^{i+1} (aixi)=iaixi1aixidx=aii+11xi+1

多项式牛顿迭代

直接上结论……
其实我也不会推
若 G ( F ( x ) ) ≡ 0 ( m o d   ≥ x n ) 已 知 G ( F 0 ( x ) ) ≡ 0 ( m o d   n 2 ) 则 F ( x ) ≡ F 0 ( x ) − G ( F 0 ( x ) ) G ′ ( F 0 ( x ) ) ( m o d   x n ) 若G(F(x))\equiv 0(mod\ \geq x^n)\\ 已知G(F_0(x))\equiv 0(mod\ \frac{n}{2})\\ 则F(x)\equiv F_0(x)-\frac{G(F_0(x))}{G'(F_0(x))}(mod\ x^n) G(F(x))0(mod xn)G(F0(x))0(mod 2n)F(x)F0(x)G(F0(x))G(F0(x))(mod xn)
套路的将 A A A 化成常数,造出关于 B B B 的函数,然后带公式
于是很多问题可以倍增解决

多项式exp

e A ( x ) ≡ B ( x ) ( m o d   x n ) e^{A(x)}\equiv B(x)(mod\ x^n) eA(x)B(x)(mod xn)
AB
e A ( x ) ≡ B ( x ) ( m o d   x n ) 设 G ( B ( x ) ) ≡ l n B ( x ) − A ( x ) ( m o d   x n ) 有 G ( B ( x ) ) ≡ 0 ( m o d   x n ) 所 以 B ( x ) ≡ B 0 ( x ) − G ( B 0 ( x ) ) G ′ ( B 0 ( x ) ) ( m o d   x n ) 求 导 有 G ′ ( B ( x ) ) ≡ B ( x ) − 1 ( m o d   x n ) 代 入 有 B ( x ) ≡ B 0 ( x ) − l n B 0 ( x ) − A ( x ) B 0 ( x ) − 1 ( m o d   x n ) B ( x ) ≡ B 0 ( x ) − ( l n B 0 ( x ) − A ( x ) ) B 0 ( x ) ( m o d   x n ) B ( x ) ≡ ( 1 − l n B 0 ( x ) + A ( x ) ) B 0 ( x ) ( m o d   x n ) 只 有 常 数 项 时 B 0 = e 0 = 1 e^{A(x)}\equiv B(x)(mod\ x^n)\\ 设G(B(x))\equiv lnB(x)-A(x)(mod\ x^n)\\ 有G(B(x))\equiv 0(mod\ x^n)\\ 所以B(x)\equiv B_0(x)-\frac{G(B_0(x))}{G'(B_0(x))}(mod\ x^n)\\ 求导有G'(B(x))\equiv B(x)^{-1}(mod\ x^n)\\ 代入有B(x)\equiv B_0(x)-\frac{lnB_0(x)-A(x)}{B_0(x)^{-1}}(mod\ x^n)\\ B(x)\equiv B_0(x)-(lnB_0(x)-A(x))B_0(x)(mod\ x^n)\\ B(x)\equiv (1-lnB_0(x)+A(x))B_0(x)(mod\ x^n)\\ 只有常数项时B_0=e^0=1 eA(x)B(x)(mod xn)G(B(x))lnB(x)A(x)(mod xn)G(B(x))0(mod xn)B(x)B0(x)G(B0(x))G(B0(x))(mod xn)G(B(x))B(x)1(mod xn)B(x)B0(x)B0(x)1lnB0(x)A(x)(mod xn)B(x)B0(x)(lnB0(x)A(x))B0(x)(mod xn)B(x)(1lnB0(x)+A(x))B0(x)(mod xn)B0=e0=1
倍增即可

多项式快速幂

B ( x ) ≡ A k ( x ) ( m o d   x n ) B(x)\equiv A^k(x)(mod\ x^n) B(x)Ak(x)(mod xn)
AB
公式十分简单 A k ( x ) = e l n ( A ( x ) ) × k A^k(x)=e^{ln(A(x))\times k} Ak(x)=eln(A(x))×k
于是一个求 ln,卷一个常数,再 exp 即可
对于常数项不为 1 的情况,如果有零,先左移 num 位到没有零,结果再右移 num*k 位零,然后可以整体除以常数项 a 0 a_0 a0,最后再乘 a 0 k a_0^k a0k

有零的模板:

#include <bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long

inline char ch() {
  static char buf[1 << 21], *p1 = buf, *p2 = buf;
  return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2)
             ? EOF
             : *p1++;
}

inline int in {
  int s = 0, f = 1;
  char x;
  for (x = ch(); x < '0' || x > '9'; x = ch())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}

const int A = 1e6 + 5;
const int mod = 998244353;
const int phi = mod - 1;
int r[A], lim;

inline int power(int s, int c) {
  int ans = 1;
  while (c) {
    if (c & 1) ans = ans * s % mod;
    s = s * s % mod;
    c >>= 1;
  }
  return ans;
}

inline void init(int len) {
  lim = 1;
  while (lim < len) lim <<= 1;
  for (int i = 1; i < lim; i++)
    r[i] = (r[i >> 1] >> 1) | ((i & 1) ? lim >> 1 : 0);
  return;
}

inline void ntt(int *Q, int pos) {
  for (int i = 0; i < lim; i++)
    if (i < r[i]) swap(Q[i], Q[r[i]]);
  for (int mid = 1; mid < lim; mid <<= 1) {
    int wn = power(3, (mod - 1) / (mid << 1));
    for (int i = 0; i < lim; i += (mid << 1)) {
      int fw = 1;
      for (int j = 0; j < mid; j++, fw = fw * wn % mod) {
        int x = Q[i + j], y = Q[i + j + mid] * fw % mod;
        Q[i + j] = (x + y) % mod;
        Q[i + j + mid] = (x - y + mod) % mod;
      }
    }
  }
  if (pos) return;
  int k = power(lim, mod - 2);
  reverse(Q + 1, Q + lim);
  for (int i = 0; i < lim; i++) Q[i] = Q[i] * k % mod;
  return;
}

inline void Der(int *x, int *y, int len) {
  for (int i = 0; i < len; i++) y[i] = x[i + 1] * (i + 1) % mod;
  return;
}

int inv_ing[A];
inline void Ing(int *x, int *y, int len) {
#define inv inv_ing
  inv[1] = 1;
  for (int i = 2; i < len; i++)
    inv[i] = (-(mod / i) * inv[mod % i] % mod + mod) % mod;
  for (int i = 1; i < len; i++) y[i] = x[i - 1] * inv[i] % mod;
  y[0] = 0;
#undef inv
  return;
}

inline void Flow(int *x, int *y, int lx, int ly) {
  init(lx + ly);
  ntt(x, 1), ntt(y, 1);
  for (int i = 0; i < lim; i++) x[i] = x[i] * y[i] % mod;
  ntt(x, 0);
  return;
}

int tmp_inv[A];
inline void Inv(int *x, int *y, int len) {
#define tmp tmp_inv
  if (len == 1) {
    y[0] = power(x[0], mod - 2);
    return;
  }
  Inv(x, y, (len + 1) >> 1);
  init(len << 1);
  for (int i = 0; i < len; i++) tmp[i] = x[i];
  for (int i = len; i < lim; i++) tmp[i] = 0;
  ntt(y, 1), ntt(tmp, 1);
  for (int i = 0; i < lim; i++)
    y[i] = (2 - y[i] * tmp[i] % mod + mod) % mod * y[i] % mod;
  ntt(y, 0);
  for (int i = len; i < lim; i++) y[i] = 0;
  for (int i = 0; i < lim; i++) tmp[i] = 0;
#undef tmp
}

int p1_ln[A], p2_ln[A];
inline void Ln(int *x, int *y, int len) {
#define p1 p1_ln
#define p2 p2_ln
  Der(x, p1, len);
  Inv(x, p2, len);
  Flow(p1, p2, len, len);
  Ing(p1, y, len);
  for (int i = len; i < lim; i++) y[i] = 0;
  for (int i = 0; i < lim; i++) p1[i] = p2[i] = 0;
  return;
#undef p1
#undef p2
}

int tmp_exp[A];
inline void Exp(int *x, int *y, int len) {
#define tmp tmp_exp
  if (len == 1) {
    y[0] = 1;
    return;
  }
  Exp(x, y, (len + 1) >> 1);
  Ln(y, tmp, len);
  init(len << 1);
  for (int i = 0; i < len; i++) tmp[i] = (x[i] - tmp[i] + mod) % mod;
  tmp[0] = (tmp[0] + 1) % mod;
  for (int i = len; i < lim; i++) tmp[i] = 0;
  Flow(y, tmp, len, len);
  for (int i = len; i < lim; i++) y[i] = 0;
  for (int i = 0; i < lim; i++) tmp[i] = 0;
#undef tmp
  return;
}

int tmp_power[A];
inline void Power(int *x, int *y, int len, int c) {
#define tmp tmp_power
  Ln(x, tmp, len);
  for (int i = 0; i < len; i++) tmp[i] = tmp[i] * c % mod;
  Exp(tmp, y, len);
#undef tmp
  return;
}

int n, K, PK, maxx;
int a[A], b[A];
int num, mul;

inline void prepare() {
  for (num = 0; num < n; num++)
    if (a[num]) break;
  for (int i = num; i < n; i++) a[i - num] = a[i];
  mul = a[0];
  int k = power(a[0], mod - 2);
  for (int i = 0; i < n; i++) a[i] = a[i] * k % mod;
  return;
}

inline void print() {
  if (num * maxx >= n) {
    for (int i = 0; i < n; i++) printf("0 ");
    return;
  }
  for (int i = 0; i < num * maxx; i++) printf("0 ");
  int k = power(mul, PK);
  for (int i = 0; i < n - num; i++) b[i] = b[i] * k % mod;
  for (int i = 0; i < n - num * maxx; i++) printf("%lld ", b[i]);
  return;
}

signed main() {
  n = in;
  char x;
  while (x < '0' || x > '9') x = ch();
  while (x >= '0' && x <= '9') {
    K = ((K * 10) % mod + (x & 15)) % mod;
    PK = ((PK * 10) % phi + (x & 15)) % phi;
    if ((maxx * 10) + (x & 15) < mod) maxx = (maxx * 10) + (x & 15);
    x = ch();
  }
  for (int i = 0; i < n; i++) a[i] = in;
  prepare();
  if (num * maxx >= n) {
    print();
    return 0;
  }
  Power(a, b, n - num, K);
  print();
  return 0;
}

多项式开根

A ( x ) ≡ B ( x ) ( m o d   x n ) \sqrt{A(x)}\equiv B(x)(mod\ x^n) A(x) B(x)(mod xn)
AB
有 B ( x ) 2 − A ( x ) ≡ 0 ( m o d   x n ) 设 G ( B ( x ) ) ≡ B ( x ) 2 − A ( x ) ( m o d   x n ) 有 B ( x ) ≡ B 0 ( x ) − G ( B 0 ( x ) ) G ′ ( B 0 ( x ) ) ( m o d   x n ) 因 为 G ′ ( B ( x ) ) ≡ 2 B ( x ) ( m o d   x n ) 所 以 B ( x ) ≡ B 0 ( x ) − B 0 2 ( x ) − A ( x ) 2 B 0 ( x ) ( m o d   x n ) B ( x ) ≡ A ( x ) + B 0 2 ( x ) 2 B 0 ( x ) ( m o d   x n ) B ( x ) ≡ 1 2 ( A ( x ) B 0 ( x ) + B 0 ( x ) ) ( m o d   x n ) 有B(x)^2-A(x)\equiv 0(mod\ x^n)\\ 设G(B(x))\equiv B(x)^2-A(x)(mod\ x^n)\\ 有B(x)\equiv B_0(x)-\frac {G(B_0(x))}{G'(B_0(x))}(mod\ x^n)\\ 因为G'(B(x))\equiv 2B(x)(mod\ x^n)\\ 所以B(x)\equiv B_0(x)-\frac{B_0^2(x)-A(x)}{2B_0(x)}(mod\ x^n)\\ B(x)\equiv \frac{A(x)+B_0^2(x)}{2B_0(x)}(mod\ x^n)\\ B(x)\equiv \frac{1}{2} (\frac{A(x)}{B_0(x)}+B_0(x))(mod\ x^n) B(x)2A(x)0(mod xn)G(B(x))B(x)2A(x)(mod xn)B(x)B0(x)G(B0(x))G(B0(x))(mod xn)G(B(x))2B(x)(mod xn)B(x)B0(x)2B0(x)B02(x)A(x)(mod xn)B(x)2B0(x)A(x)+B02(x)(mod xn)B(x)21(B0(x)A(x)+B0(x))(mod xn)
倍增即可
当多项式只有常数项时,开根的结果是二次剩余,题目保证 a 0 = 1 a_0=1 a0=1,那么直接赋为 1 即可

多项式除法

F ( x ) = Q ( x ) ∗ G ( x ) + R ( x ) F(x)=Q(x)*G(x)+R(x) F(x)=Q(x)G(x)+R(x)
F 和 G F和G FG Q 和 R Q和R QR
将 1 x 代 入 有 F ( 1 x ) = Q ( 1 x ) ∗ G ( 1 x ) + R ( 1 x ) 发 现 x n F ( 1 x ) 与 F ( x ) 的 系 数 颠 倒 令 F R ( x ) = x n F ( x ) 于 是 x n F ( 1 x ) = x n Q ( 1 x ) ∗ G ( 1 x ) + x n R ( 1 x ) x n F ( 1 x ) = x n − m Q ( 1 x ) ∗ x m G ( 1 x ) + x n − m + 1 x m − 1 R ( 1 x ) F R ( x ) = Q R ( x ) ∗ G R ( x ) + x n − m + 1 R R ( x ) F R ( x ) = Q R ( x ) ∗ G R ( x ) ( m o d   x n − m + 1 ) Q R ( x ) = F R ( x ) ∗ G R − 1 ( x ) ( m o d   x n − m + 1 ) 将 \frac{1}{x} 代入\\ 有F(\frac{1}{x})=Q(\frac{1}{x})*G(\frac{1}{x})+R(\frac{1}{x})\\ 发现x^n F(\frac{1}{x})与F(x)的系数颠倒\\ 令F_R(x)=x^n F(x)\\ 于是x^n F(\frac{1}{x})=x^n Q(\frac{1}{x})*G(\frac{1}{x})+x^n R(\frac{1}{x})\\ x^n F(\frac{1}{x})=x^{n-m} Q(\frac{1}{x})*x^m G(\frac{1}{x})+x^{n-m+1}x^{m-1} R(\frac{1}{x})\\ F_R(x)=Q_R(x)*G_R(x)+x^{n-m+1}R_R(x)\\ F_R(x)=Q_R(x)*G_R(x)(mod\ x^{n-m+1})\\ Q_R(x)=F_R(x)*G_R^{-1}(x)(mod\ x^{n-m+1}) x1F(x1)=Q(x1)G(x1)+R(x1)xnF(x1)F(x)FR(x)=xnF(x)xnF(x1)=xnQ(x1)G(x1)+xnR(x1)xnF(x1)=xnmQ(x1)xmG(x1)+xnm+1xm1R(x1)FR(x)=QR(x)GR(x)+xnm+1RR(x)FR(x)=QR(x)GR(x)(mod xnm+1)QR(x)=FR(x)GR1(x)(mod xnm+1)
反转系数后对 G 求逆,卷上 F 再反转系数就能求出 Q
然后 R ( x ) = F ( x ) − Q ( x ) ∗ G ( x ) R(x)=F(x)-Q(x)*G(x) R(x)=F(x)Q(x)G(x) 即可

多项式多点求值

给出多项式 F(x),求其在 x 1 , x 2 … … x n x_1,x_2……x_n x1,x2xn 处的值
即求 f ( x 1 ) , f ( x 2 ) … … f ( x n ) f(x_1),f(x_2)……f(x_n) f(x1),f(x2)f(xn)
考虑构造 G(x)H(x)
g ( x ) = ∏ i = 1 n 2 ( x − x i ) h ( x ) = ∏ i = n 2 + 1 n ( x − x i ) g(x)=\prod_{i=1}^{\frac{n}{2}} (x-x_i)\\ h(x)=\prod_{i=\frac{n}{2} +1}^{n} (x-x_i) g(x)=i=12n(xxi)h(x)=i=2n+1n(xxi)

f ( x i ) = ( F % G ) ( x i ) ( i ≤ n 2 ) f ( x i ) = ( F % H ) ( x i ) ( i > n 2 ) f(x_i)=(F\% G)(x_i)(i\leq \frac{n}{2})\\ f(x_i)=(F\% H)(x_i)(i> \frac{n}{2}) f(xi)=(F%G)(xi)(i2n)f(xi)=(F%H)(xi)(i>2n)
对于两边分治递归处理

多项式全家桶

#include <bits/stdc++.h>
using namespace std;
#define re register
#define int long long
namespace IO {
char _buf[1 << 21], *_p1 = _buf, *_p2 = _buf;
#define ch()                                                                 \
  (_p1 == _p2 &&                                                             \
           (_p2 = (_p1 = _buf) + fread(_buf, 1, 1 << 21, stdin), _p1 == _p2) \
       ? EOF                                                                 \
       : *_p1++)
inline int in() {
  int s = 0, f = 1;
  char x = ch();
  while (x < '0' || x > '9') {
    if (x == '-') f = -1;
    x = ch();
  }
  while (x >= '0' && x <= '9') {
    s = (s * 10) + (x & 15);
    x = ch();
  }
  return f = 1 ? s : -s;
}
char _buf_[1 << 21];
int _p1_ = -1;
inline void flush() {
  fwrite(_buf_, 1, _p1_ + 1, stdout);
  _p1_ = -1;
}
inline void pc(char x) {
  if (_p1_ == (1 << 21) - 1) flush();
  _buf_[++_p1_] = x;
}
inline void out(int x) {
  char k[20];
  int tot = 0;
  if (!x) {
    pc('0');
    return;
  }
  if (x < 0) {
    pc('-');
    x = -x;
  }
  while (x) {
    k[++tot] = (x % 10) | 48;
    x /= 10;
  }
  for (int i = tot; i; i--) pc(k[i]);
  return;
}
}  // namespace IO
using namespace IO;
typedef vector<int> poly;
const int A = 3e5 + 5;
const int logA = 18;
const int mod = 998244353;
inline int add(int a, int b) { return a + b >= mod ? a + b - mod : a + b; }
inline int dec(int a, int b) { return a - b < 0 ? a - b + mod : a - b; }
inline int mul(int a, int b) { return a * b % mod; }
inline void Add(int &a, int b) {
  a = add(a, b);
  return;
}
inline void Dec(int &a, int b) {
  a = dec(a, b);
  return;
}
inline void Mul(int &a, int b) {
  a = mul(a, b);
  return;
}
inline int power(int s, int c) {
  int ans = 1;
  while (c) {
    if (c & 1) Mul(ans, s);
    s = mul(s, s);
    c >>= 1;
  }
  return ans;
}
inline void print(poly a) {
  for (int i = 0; i < a.size(); i++) out(a[i]), pc(' ');
  pc('\n');
  return;
}
int _r[A], _lim;
inline void init(int len) {
  _lim = 1;
  while (_lim < len) _lim <<= 1;
  for (int i = 1; i < _lim; i++)
    _r[i] = (_r[i >> 1] >> 1) | ((i & 1) ? _lim >> 1 : 0);
  return;
}
inline poly operator+(poly a, poly b) {
  if (a.size() < b.size()) a.resize(b.size());
  for (int i = 0; i < b.size(); i++) Add(a[i], b[i]);
  return a;
}
inline poly operator-(poly a, poly b) {
  if (a.size() < b.size()) a.resize(b.size());
  for (int i = 0; i < b.size(); i++) Dec(a[i], b[i]);
  return a;
}
inline poly Divx(poly a) {
  for (int i = 0; i < a.size(); i++) a[i] = a[i + 1];
  a.pop_back();
  return a;
}
int *_wn[logA + 1], inv[A], fact[A];
inline void pre_ntt() {
  for (int i = 1; i <= logA; i++) _wn[i] = new int[1 << (i - 1)];
  int k = power(3, (mod - 1) / (1 << logA));
  _wn[logA][0] = 1;
  for (int i = 1; i < (1 << (logA - 1)); i++)
    _wn[logA][i] = mul(_wn[logA][i - 1], k);
  for (int i = logA - 1; i; i--)
    for (int j = 0; j < (1 << (i - 1)); j++) _wn[i][j] = _wn[i + 1][j << 1];
  inv[0] = inv[1] = 1;
  for (int i = 2; i <= (1 << logA); i++)
    inv[i] = mul(mod - mod / i, inv[mod % i]);
  fact[0] = 1;
  for (int i = 1; i <= (1 << logA); i++) fact[i] = mul(fact[i - 1], i);
  return;
}
inline void ntt(poly &Q, int pos) {
  for (int i = 0; i < _lim; i++)
    if (i < _r[i]) swap(Q[_r[i]], Q[i]);
  for (int mid = 1, c = 1; mid < _lim; mid <<= 1, c++)
    for (int i = 0; i < _lim; i += (mid << 1))
      for (int j = 0; j < mid; j++) {
        int x = Q[i + j], y = mul(Q[i + j + mid], _wn[c][j]);
        Q[i + j] = add(x, y);
        Q[i + j + mid] = dec(x, y);
      }
  if (pos) return;
  int k = power(_lim, mod - 2);
  reverse(Q.begin() + 1, Q.end());
  for (int i = 0; i < _lim; i++) Mul(Q[i], k);
  return;
}
inline poly operator*(poly a, poly b) {
  int len = a.size() + b.size() - 1;
  if (a.size() < 32 || b.size() < 32) {
    poly c(len, 0);
    for (int i = 0; i < a.size(); i++)
      for (int j = 0; j < b.size(); j++) Add(c[i + j], mul(a[i], b[j]));
    return c;
  }
  init(len);
  a.resize(_lim), b.resize(_lim);
  ntt(a, 1), ntt(b, 1);
  for (int i = 0; i < _lim; i++) Mul(a[i], b[i]);
  ntt(a, 0);
  a.resize(len);
  return a;
}
inline poly Inv(poly a, int len) {
  poly b(1, power(a[0], mod - 2)), c;
  for (int deg = 2; (deg >> 1) < len; deg <<= 1) {
    c.resize(deg);
    init(deg << 1);
    for (int i = 0; i < deg; i++) c[i] = i < a.size() ? a[i] : 0;
    c.resize(_lim), b.resize(_lim);
    ntt(c, 1), ntt(b, 1);
    for (int i = 0; i < _lim; i++) Mul(b[i], dec(2, mul(b[i], c[i])));
    ntt(b, 0);
    b.resize(deg);
  }
  b.resize(len);
  return b;
}
inline poly Der(poly a) {
  for (int i = 0; i < a.size(); i++) a[i] = mul(a[i + 1], i + 1);
  a.pop_back();
  return a;
}
inline poly Ing(poly a) {
  a.push_back(0);
  for (int i = a.size() - 1; i; i--) a[i] = mul(inv[i], a[i - 1]);
  a[0] = 0;
  return a;
}
inline poly Ln(poly a, int len) {
  a = Ing(Der(a) * Inv(a, len));
  a.resize(len);
  return a;
}
inline poly Exp(poly a, int len) {
  poly b(1, 1), c;
  for (int deg = 2; deg < (len << 1); deg <<= 1) {
    c = Ln(b, deg);
    Dec(c[0], 1);
    for (int i = 0; i < deg; i++) c[i] = dec(i < a.size() ? a[i] : 0, c[i]);
    b = b * c;
    b.resize(deg);
  }
  b.resize(len);
  return b;
}
inline poly Power(poly a, int c, int len) {
  a = Ln(a, len);
  for (int i = 0; i < a.size(); i++) Mul(a[i], c);
  return Exp(a, len);
}
inline poly Sqrt(poly a, int len) {
  poly b(1, 1), c;
  for (int deg = 2; deg < (len << 1); deg <<= 1) {
    b = a * Inv(b, deg) + b;
    for (int i = 0; i < deg; i++) b[i] = mul(b[i], inv[2]);
    b.resize(deg);
  }
  b.resize(len);
  return b;
}
inline void operator-=(poly &a, const poly &b) {
  if (a.size() < b.size()) a.resize(b.size());
  for (int i = 0; i < b.size(); i++) Dec(a[i], b[i]);
  return;
}
inline poly operator/(poly a, poly b) {
  int len = a.size() - b.size() + 1;
  reverse(a.begin(), a.end());
  a.resize(len);
  reverse(b.begin(), b.end());
  a = a * Inv(b, len);
  a.resize(len);
  reverse(a.begin(), a.end());
  return a;
}
inline poly operator%(poly a, poly b) {
  if (a.size() < b.size()) return a;
  a -= b * (a / b);
  a.resize(b.size() - 1);
  return a;
}
poly _f[A];
#define lch p << 1
#define rch p << 1 | 1
inline void pre_val(int p, int l, int r, int *x) {
  if (l == r) {
    _f[p].push_back(dec(0, x[l]));
    _f[p].push_back(1);
    return;
  }
  int mid = (l + r) >> 1;
  pre_val(lch, l, mid, x), pre_val(rch, mid + 1, r, x);
  _f[p] = _f[lch] * _f[rch];
  return;
}
inline void Val(int p, int l, int r, poly F, int *v) {
  if (l == r) {
    v[l] = F[0];
    return;
  }
  int mid = (l + r) >> 1;
  Val(lch, l, mid, F % _f[lch], v), Val(rch, mid + 1, r, F % _f[rch], v);
  return;
}
#undef lch
#undef rch
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值