Skip to the content.

:heavy_check_mark: Polynomial Composite Set Power Series
(set/composite-set-power-series.hpp)

多項式 $\sum_{i=0}^{m-1}c_ix^i$,$2^n$ の集合冪級数 $a$ について $\sum_{i=0}^{m-1}c_ia^i$ を $O(n^22^m)$ 時間で計算する.

EGF との合成

$[x^0]a=0$ のとき EGF $f(x)=\sum_{k\geq 0}f_k\frac{x^k}{k!}$ との合成 $f(a)=\sum_{k\geq 0}f_k\frac{a^k}{k!}$ を考える.

$R[x_1,\dots,x_n]/(x_1^2,\dots,x_n^2)$ の元として考えると $[x_n^1]f(a)=[x_n^0]f’(a)[x_n^1]a$ が成り立つ.

従って $[x_n^0]f’(a)$ から $f(a)$ を得られる.

再帰的に計算すると長さ $2^i$ の subset convolution を $n-i$ 回行うことになり,計算量は $O(n^22^n)$ と評価できる.

多項式との合成

$[x^0]a=0$ のとき EGF との合成に帰着される.

$[x^0]a=c\neq 0$ のとき $f(x+c)$ と $a-c$ を合成すればよい. $f(x+c)$ は $n$ 次まで求めれば十分なので $O(nm)$ 時間で計算できる.

別の手法

ゼータ変換した先の多項式を FPS と合成することでも求められる.

log や pow は定数倍が改善する.

Depends on

Verified with

Code

#pragma once
#include "set/subset-convolution.hpp"

namespace SetPowerSeries {
template <class mint, int sz = 21>
vector<mint> composite_egf(vector<mint> f, vector<mint> a) {
  static SubsetConvolution<mint, sz> sc;
  assert(a[0] == 0);
  if (f.empty()) return vector<mint>(a.size());
  int l = __builtin_ctz(a.size());
  f.resize(l + 1);
  vector<vector<mint>> g(l + 1);
  for (int i = 0; i <= l; i++) g[i] = vector<mint>(1 << (l - i), 0);
  for (int i = 0; i <= l; i++) g[i][0] = f[i];
  for (int k = 0; k < l; k++) {
    auto p = sc.lift(vector<mint>(a.begin() + (1 << k), a.begin() + (2 << k)));
    sc.ranked_zeta(p);
    for (int i = 0; i < l - k; i++) {
      auto q = sc.lift(vector<mint>(g[i + 1].begin(), g[i + 1].begin() + (1 << k)));
      sc.ranked_zeta(q);
      sc.ranked_mul(q, p);
      sc.ranked_mobius(q);
      auto h = sc.unlift(q);
      copy(h.begin(), h.end(), g[i].begin() + (1 << k));
    }
  }
  return g[0];
}
template <class mint, int sz = 21>
vector<mint> composite_polynomial(vector<mint> p, vector<mint> a) {
  if (p.empty()) return vector<mint>(a.size());
  int l = __builtin_ctz(a.size());
  if (a[0] != 0) {
    mint c = a[0];
    a[0] = 0;
    vector<mint> p1(l + 1, 0), binom(l + 1, 0);
    binom[0] = 1;
    for (int i = 0; i < p.size(); i++) {
      mint r = i <= l ? 1 : c.pow(i - l);
      for (int j = min(i, l); j >= 0; j--, r *= c) p1[j] += p[i] * binom[j] * r;
      for (int j = l; j > 0; j--) binom[j] += binom[j - 1];
    }
    swap(p, p1);
  }
  mint r = 1;
  for (int i = 1; i <= l; i++) p[i] *= (r *= i);
  return composite_egf<mint, sz>(p, a);
}

// log(a), [x^0]a=1
// require inverse of 1,...,sz
template <class mint, int sz = 21>
vector<mint> log(vector<mint> a) {
  static SubsetConvolution<mint, sz> sc;
  assert(a[0] == 1);
  int l = __builtin_ctz(a.size());
  if (l == 0) return {0};
  vector<mint> inv(l + 1, 1);
  rep(i, 1, l + 1) inv[i] = mint(i).inv();
  auto p = sc.lift(a);
  sc.ranked_zeta(p);
  for (int k = 0; k < p.size(); k++) {
    auto q = p[k];
    p[k][0] = 0;
    for (int i = 1; i <= l; i++) {
      mint v = i * q[i];
      for (int j = 1; j < i; j++) v -= j * p[k][j] * q[i - j];
      p[k][i] = v * inv[i];
    }
  }
  sc.ranked_mobius(p);
  return sc.unlift(p);
}
// log(a), [x^0]a=1
// not require inverse of 1,...,sz
template <class mint, int sz = 21>
vector<mint> log_arbitrary(vector<mint> a) {
  assert(a[0] == 1);
  int l = __builtin_ctz(a.size());
  if (l == 0) return {0};
  a[0] = 0;
  vector<mint> f(l + 1, 0);
  f[1] = 1;
  for (int i = 2; i <= l; i++) f[i] = f[i - 1] * (1 - i);
  return composite_egf<mint, sz>(f, a);
}
// a^m, [x^0]a=1
// require inverse of 1,...,sz
template <class mint, int sz = 21>
vector<mint> pow(vector<mint> a, mint m) {
  static SubsetConvolution<mint, sz> sc;
  assert(a[0] == 1);
  int l = __builtin_ctz(a.size());
  if (l == 0) return {1};
  vector<mint> inv(l + 1, 1);
  rep(i, 1, l + 1) inv[i] = mint(i).inv();
  auto p = sc.lift(a);
  sc.ranked_zeta(p);
  for (int k = 0; k < p.size(); k++) {
    auto q = p[k];
    p[k][0] = 1;
    for (int i = 1; i <= l; i++) {
      mint v = 0;
      for (int j = 1; j < i; j++) v += (m * j - (i - j)) * p[k][i - j] * q[j];
      v *= inv[i];
      v += m * p[k][0] * q[i];
      p[k][i] = v;
    }
  }
  sc.ranked_mobius(p);
  return sc.unlift(p);
}
// a^m, [x^0]a=1
// not require inverse of 1,...,sz
template <class mint, int sz = 21>
vector<mint> pow_arbitrary(vector<mint> a, mint m) {
  assert(a[0] == 1);
  int l = __builtin_ctz(a.size());
  if (l == 0) return {1};
  a[0] = 0;
  vector<mint> f(l + 1, 0);
  f[0] = 1;
  for (int i = 1; i <= l; i++) {
    f[i] = f[i - 1] * m;
    m -= 1;
  }
  return composite_egf<mint, sz>(f, a);
}
};  // namespace SetPowerSeries
/**
 * @brief Polynomial Composite Set Power Series
 * @docs docs/set/composite-set-power-series.md
 */
#line 2 "set/subset-convolution.hpp"

template <class mint, int n_>
struct SubsetConvolution {
  static constexpr int n = n_;
  using poly = array<mint, n_ + 1>;
  vector<int> pc;
  SubsetConvolution() {
    pc.assign(1 << n, 0);
    for (int i = 1; i < pc.size(); i++) pc[i] = pc[i >> 1] + (i & 1);
  }
  void poly_add(poly& p, const poly& q, int d) {
    for (int i = 0; i < d; i++) p[i] += q[i];
  }
  void poly_sub(poly& p, const poly& q, int d) {
    for (int i = d; i <= n; i++) p[i] -= q[i];
  }
  void poly_mul(poly& p, const poly& q) {
    poly r{};
    for (int i = 0; i <= n; i++)
      for (int j = 0; j <= n - i; j++)
        r[i + j] += p[i] * q[j];
    swap(p, r);
  }
  vector<poly> lift(const vector<mint>& a) {
    int n = a.size();
    assert(n == (n & -n));
    vector<poly> b(n);
    for (int i = 0; i < n; i++) {
      b[i].fill(0);
      b[i][pc[i]] = a[i];
    }
    return b;
  }
  vector<mint> unlift(const vector<poly>& b) {
    int n = b.size();
    assert(n == (n & -n));
    vector<mint> a(n);
    for (int i = 0; i < n; i++) a[i] = b[i][pc[i]];
    return a;
  }
  void ranked_zeta(vector<poly>& a) {
    int n = a.size();
    for (int i = 1; i < n; i <<= 1)
      for (int j = 0; j < n; j += i * 2)
        for (int k = 0; k < i; k++)
          poly_add(a[i + j + k], a[j + k], pc[i + j + k]);
  }
  void ranked_mobius(vector<poly>& a) {
    int n = a.size();
    for (int i = 1; i < n; i <<= 1)
      for (int j = 0; j < n; j += i * 2)
        for (int k = 0; k < i; k++)
          poly_sub(a[i + j + k], a[j + k], pc[i + j + k]);
  }
  void ranked_mul(vector<poly>& a, const vector<poly>& b) {
    for (int i = 0; i < a.size(); i++) poly_mul(a[i], b[i]);
  }
  vector<mint> multiply(const vector<mint>& a, const vector<mint>& b) {
    auto p = lift(a);
    auto q = lift(b);
    ranked_zeta(p);
    ranked_zeta(q);
    ranked_mul(p, q);
    ranked_mobius(p);
    return unlift(p);
  }
};

/**
 * @brief Subset Convolution
 * @docs docs/set/subset-convolution.md
 */
#line 3 "set/composite-set-power-series.hpp"

namespace SetPowerSeries {
template <class mint, int sz = 21>
vector<mint> composite_egf(vector<mint> f, vector<mint> a) {
  static SubsetConvolution<mint, sz> sc;
  assert(a[0] == 0);
  if (f.empty()) return vector<mint>(a.size());
  int l = __builtin_ctz(a.size());
  f.resize(l + 1);
  vector<vector<mint>> g(l + 1);
  for (int i = 0; i <= l; i++) g[i] = vector<mint>(1 << (l - i), 0);
  for (int i = 0; i <= l; i++) g[i][0] = f[i];
  for (int k = 0; k < l; k++) {
    auto p = sc.lift(vector<mint>(a.begin() + (1 << k), a.begin() + (2 << k)));
    sc.ranked_zeta(p);
    for (int i = 0; i < l - k; i++) {
      auto q = sc.lift(vector<mint>(g[i + 1].begin(), g[i + 1].begin() + (1 << k)));
      sc.ranked_zeta(q);
      sc.ranked_mul(q, p);
      sc.ranked_mobius(q);
      auto h = sc.unlift(q);
      copy(h.begin(), h.end(), g[i].begin() + (1 << k));
    }
  }
  return g[0];
}
template <class mint, int sz = 21>
vector<mint> composite_polynomial(vector<mint> p, vector<mint> a) {
  if (p.empty()) return vector<mint>(a.size());
  int l = __builtin_ctz(a.size());
  if (a[0] != 0) {
    mint c = a[0];
    a[0] = 0;
    vector<mint> p1(l + 1, 0), binom(l + 1, 0);
    binom[0] = 1;
    for (int i = 0; i < p.size(); i++) {
      mint r = i <= l ? 1 : c.pow(i - l);
      for (int j = min(i, l); j >= 0; j--, r *= c) p1[j] += p[i] * binom[j] * r;
      for (int j = l; j > 0; j--) binom[j] += binom[j - 1];
    }
    swap(p, p1);
  }
  mint r = 1;
  for (int i = 1; i <= l; i++) p[i] *= (r *= i);
  return composite_egf<mint, sz>(p, a);
}

// log(a), [x^0]a=1
// require inverse of 1,...,sz
template <class mint, int sz = 21>
vector<mint> log(vector<mint> a) {
  static SubsetConvolution<mint, sz> sc;
  assert(a[0] == 1);
  int l = __builtin_ctz(a.size());
  if (l == 0) return {0};
  vector<mint> inv(l + 1, 1);
  rep(i, 1, l + 1) inv[i] = mint(i).inv();
  auto p = sc.lift(a);
  sc.ranked_zeta(p);
  for (int k = 0; k < p.size(); k++) {
    auto q = p[k];
    p[k][0] = 0;
    for (int i = 1; i <= l; i++) {
      mint v = i * q[i];
      for (int j = 1; j < i; j++) v -= j * p[k][j] * q[i - j];
      p[k][i] = v * inv[i];
    }
  }
  sc.ranked_mobius(p);
  return sc.unlift(p);
}
// log(a), [x^0]a=1
// not require inverse of 1,...,sz
template <class mint, int sz = 21>
vector<mint> log_arbitrary(vector<mint> a) {
  assert(a[0] == 1);
  int l = __builtin_ctz(a.size());
  if (l == 0) return {0};
  a[0] = 0;
  vector<mint> f(l + 1, 0);
  f[1] = 1;
  for (int i = 2; i <= l; i++) f[i] = f[i - 1] * (1 - i);
  return composite_egf<mint, sz>(f, a);
}
// a^m, [x^0]a=1
// require inverse of 1,...,sz
template <class mint, int sz = 21>
vector<mint> pow(vector<mint> a, mint m) {
  static SubsetConvolution<mint, sz> sc;
  assert(a[0] == 1);
  int l = __builtin_ctz(a.size());
  if (l == 0) return {1};
  vector<mint> inv(l + 1, 1);
  rep(i, 1, l + 1) inv[i] = mint(i).inv();
  auto p = sc.lift(a);
  sc.ranked_zeta(p);
  for (int k = 0; k < p.size(); k++) {
    auto q = p[k];
    p[k][0] = 1;
    for (int i = 1; i <= l; i++) {
      mint v = 0;
      for (int j = 1; j < i; j++) v += (m * j - (i - j)) * p[k][i - j] * q[j];
      v *= inv[i];
      v += m * p[k][0] * q[i];
      p[k][i] = v;
    }
  }
  sc.ranked_mobius(p);
  return sc.unlift(p);
}
// a^m, [x^0]a=1
// not require inverse of 1,...,sz
template <class mint, int sz = 21>
vector<mint> pow_arbitrary(vector<mint> a, mint m) {
  assert(a[0] == 1);
  int l = __builtin_ctz(a.size());
  if (l == 0) return {1};
  a[0] = 0;
  vector<mint> f(l + 1, 0);
  f[0] = 1;
  for (int i = 1; i <= l; i++) {
    f[i] = f[i - 1] * m;
    m -= 1;
  }
  return composite_egf<mint, sz>(f, a);
}
};  // namespace SetPowerSeries
/**
 * @brief Polynomial Composite Set Power Series
 * @docs docs/set/composite-set-power-series.md
 */
Back to top page