library

This documentation is automatically generated by online-judge-tools/verification-helper

View the Project on GitHub maspypy/library

:warning: linalg/ecottea_matrix_dp.hpp

Depends on

Code

#include "mod/modint.hpp"
#include "string/split.hpp"

// https://maspypy.com/ecottea_dp_estimate_method
// https://atcoder.jp/contests/agc022/tasks/agc022_e
// https://atcoder.jp/contests/toyota2023spring-final/tasks/toyota2023spring_final_f
template <typename mint, int d>
struct ecottea_matrix_dp {
  using MAT = array<array<mint, d>, d>;
  string alphabet;
  map<char, MAT> matrix;
  array<mint, d> X;
  int rank;

  // naive(n): map<string,mint>
  template <typename F>
  void build(string alphabet_, F naive, int max_append = -1) {
    alphabet = alphabet_;
    vc<string> lst;
    if (max_append == -1) max_append = d - 1;
    lst.eb("");
    for (int p = 0; p < len(lst); ++p) {
      string S = lst[p];
      if (max_append < len(S)) break;
      for (auto& a : alphabet) lst.eb(S + a);
    }
    int n = len(lst);

    auto get = [&](string S) -> vc<mint> {
      vc<mint> ANS(n);
      FOR(j, n) ANS[j] = naive(lst[j] + S);
      return ANS;
    };

    vv(mint, mat, d, n);
    vc<string> basis;
    vc<int> pivot(d, infty<int>);
    vv(mint, way, d, d);
    deque<string> que;
    que.eb("");

    auto reduce = [&](vc<mint>& A) -> vc<mint> {
      vc<mint> cf(d);
      FOR(i, len(basis)) {
        mint x = A[pivot[i]];
        FOR(j, pivot[i], n) { A[j] -= x * mat[i][j]; }
        FOR(j, len(basis)) { cf[j] -= x * way[i][j]; }
      }
      return cf;
    };

    while (len(que)) {
      int p = len(basis);
      string S = POP(que);
      vc<mint> row = get(S);
      vc<mint> cf = reduce(row);
      int k = n;
      FOR_R(j, n) if (row[j] != 0) k = j;
      if (k == n) continue;
      if (p == d) {
        print("dim>d");
        assert(0);
      }
      cf[p] += 1;
      mint c = mint(1) / row[k];
      FOR(j, n) row[j] *= c;
      FOR(j, p + 1) cf[j] *= c;
      basis.eb(S);
      pivot[p] = k, mat[p] = row, way[p] = cf;
      ++p;

      auto I = argsort(pivot);
      way = rearrange(way, I);
      mat = rearrange(mat, I);
      pivot = rearrange(pivot, I);
      for (auto& ch : alphabet) {
        que.eb(ch + S);
      }
    }
    rank = len(basis);

    for (auto& ch : alphabet) {
      MAT ans{};
      FOR(i, len(basis)) {
        vc<mint> row = get(ch + basis[i]);
        vc<mint> cf = reduce(row);
        FOR(j, n) assert(row[j] == 0);
        FOR(j, len(cf)) ans[i][j] = -cf[j];
      }
      matrix[ch] = ans;
    }
    FOR(i, len(basis)) X[i] = naive(basis[i]);
    return;
  }

  string to_string(mint x) {
    int a = x.val;
    int b = a - (mint::get_mod());
    return ::to_string(abs(a) <= abs(b) ? a : b);
  }
  string to_string() {
    string out;
    out += alphabet + ".";
    FOR(i, d) out += to_string(X[i].val) + ".";
    for (auto& ch : alphabet) {
      FOR(i, d) FOR(j, d) { out += to_string(matrix[ch][i][j].val) + "."; }
    }
    out.pop_back();
    return out;
  }
  void build_from_string(string S) {
    auto dat = split(S, '.');
    alphabet = dat[0];
    int p = 1;
    FOR(i, d) X[i] = stoi(dat[p++]);
    for (auto& ch : alphabet) {
      FOR(i, d) FOR(j, d) { matrix[ch][i][j] = stoi(dat[p++]); }
    }
  }

  // ? は全部の重ね合わせとして
  mint solve(string S) {
    // 行ベクトルに行列をかけていって第0成分をとる
    MAT ques{};
    for (auto& [ch, A] : matrix) {
      FOR(i, d) FOR(j, d) ques[i][j] += A[i][j];
    }
    matrix['?'] = ques;
    array<mint, d> dp = X;
    for (auto& ch : S) {
      array<mint, d> newdp{};
      assert(matrix.count(ch));
      MAT& mat = matrix[ch];
      FOR(i, d) FOR(j, d) newdp[i] += mat[i][j] * dp[j];
      swap(dp, newdp);
    }
    return dp[0];
  }
};
#line 2 "mod/modint_common.hpp"

struct has_mod_impl {
  template <class T>
  static auto check(T &&x) -> decltype(x.get_mod(), std::true_type{});
  template <class T>
  static auto check(...) -> std::false_type;
};

template <class T>
class has_mod : public decltype(has_mod_impl::check<T>(std::declval<T>())) {};

template <typename mint>
mint inv(int n) {
  static const int mod = mint::get_mod();
  static vector<mint> dat = {0, 1};
  assert(0 <= n);
  if (n >= mod) n %= mod;
  while (len(dat) <= n) {
    int k = len(dat);
    int q = (mod + k - 1) / k;
    dat.eb(dat[k * q - mod] * mint::raw(q));
  }
  return dat[n];
}

template <>
double inv<double>(int n) {
  assert(n != 0);
  return 1.0 / n;
}

template <typename mint>
mint fact(int n) {
  static const int mod = mint::get_mod();
  assert(0 <= n && n < mod);
  static vector<mint> dat = {1, 1};
  while (len(dat) <= n) dat.eb(dat[len(dat) - 1] * mint(len(dat)));
  return dat[n];
}

template <typename mint>
mint fact_inv(int n) {
  static vector<mint> dat = {1, 1};
  if (n < 0) return mint(0);
  while (len(dat) <= n) dat.eb(dat[len(dat) - 1] * inv<mint>(len(dat)));
  return dat[n];
}

template <class mint, class... Ts>
mint fact_invs(Ts... xs) {
  return (mint(1) * ... * fact_inv<mint>(xs));
}

template <typename mint, class Head, class... Tail>
mint multinomial(Head &&head, Tail &&...tail) {
  return fact<mint>(head) * fact_invs<mint>(std::forward<Tail>(tail)...);
}

template <typename mint>
mint C_dense(int n, int k) {
  assert(n >= 0);
  if (k < 0 || n < k) return 0;
  static vvc<mint> C;
  static int H = 0, W = 0;
  auto calc = [&](int i, int j) -> mint {
    if (i == 0) return (j == 0 ? mint(1) : mint(0));
    return C[i - 1][j] + (j ? C[i - 1][j - 1] : 0);
  };
  if (W <= k) {
    FOR(i, H) {
      C[i].resize(k + 1);
      FOR(j, W, k + 1) { C[i][j] = calc(i, j); }
    }
    W = k + 1;
  }
  if (H <= n) {
    C.resize(n + 1);
    FOR(i, H, n + 1) {
      C[i].resize(W);
      FOR(j, W) { C[i][j] = calc(i, j); }
    }
    H = n + 1;
  }
  return C[n][k];
}

template <typename mint, bool large = false, bool dense = false>
mint C(ll n, ll k) {
  assert(n >= 0);
  if (k < 0 || n < k) return 0;
  if constexpr (dense) return C_dense<mint>(n, k);
  if constexpr (!large) return multinomial<mint>(n, k, n - k);
  k = min(k, n - k);
  mint x(1);
  FOR(i, k) x *= mint(n - i);
  return x * fact_inv<mint>(k);
}

template <typename mint, bool large = false>
mint C_inv(ll n, ll k) {
  assert(n >= 0);
  assert(0 <= k && k <= n);
  if (!large) return fact_inv<mint>(n) * fact<mint>(k) * fact<mint>(n - k);
  return mint(1) / C<mint, 1>(n, k);
}

// [x^d](1-x)^{-n}
template <typename mint, bool large = false, bool dense = false>
mint C_negative(ll n, ll d) {
  assert(n >= 0);
  if (d < 0) return mint(0);
  if (n == 0) {
    return (d == 0 ? mint(1) : mint(0));
  }
  return C<mint, large, dense>(n + d - 1, d);
}
#line 3 "mod/modint.hpp"

template <int mod>
struct modint {
  static constexpr u32 umod = u32(mod);
  static_assert(umod < u32(1) << 31);
  u32 val;

  static modint raw(u32 v) {
    modint x;
    x.val = v;
    return x;
  }
  constexpr modint() : val(0) {}
  constexpr modint(u32 x) : val(x % umod) {}
  constexpr modint(u64 x) : val(x % umod) {}
  constexpr modint(u128 x) : val(x % umod) {}
  constexpr modint(int x) : val((x %= mod) < 0 ? x + mod : x){};
  constexpr modint(ll x) : val((x %= mod) < 0 ? x + mod : x){};
  constexpr modint(i128 x) : val((x %= mod) < 0 ? x + mod : x){};
  bool operator<(const modint &other) const { return val < other.val; }
  modint &operator+=(const modint &p) {
    if ((val += p.val) >= umod) val -= umod;
    return *this;
  }
  modint &operator-=(const modint &p) {
    if ((val += umod - p.val) >= umod) val -= umod;
    return *this;
  }
  modint &operator*=(const modint &p) {
    val = u64(val) * p.val % umod;
    return *this;
  }
  modint &operator/=(const modint &p) {
    *this *= p.inverse();
    return *this;
  }
  modint operator-() const { return modint::raw(val ? mod - val : u32(0)); }
  modint operator+(const modint &p) const { return modint(*this) += p; }
  modint operator-(const modint &p) const { return modint(*this) -= p; }
  modint operator*(const modint &p) const { return modint(*this) *= p; }
  modint operator/(const modint &p) const { return modint(*this) /= p; }
  bool operator==(const modint &p) const { return val == p.val; }
  bool operator!=(const modint &p) const { return val != p.val; }
  modint inverse() const {
    int a = val, b = mod, u = 1, v = 0, t;
    while (b > 0) {
      t = a / b;
      swap(a -= t * b, b), swap(u -= t * v, v);
    }
    return modint(u);
  }
  modint pow(ll n) const {
    if (n < 0) return inverse().pow(-n);
    assert(n >= 0);
    modint ret(1), mul(val);
    while (n > 0) {
      if (n & 1) ret *= mul;
      mul *= mul;
      n >>= 1;
    }
    return ret;
  }
  static constexpr int get_mod() { return mod; }
  // (n, r), r は 1 の 2^n 乗根
  static constexpr pair<int, int> ntt_info() {
    if (mod == 120586241) return {20, 74066978};
    if (mod == 167772161) return {25, 17};
    if (mod == 469762049) return {26, 30};
    if (mod == 754974721) return {24, 362};
    if (mod == 880803841) return {23, 211};
    if (mod == 943718401) return {22, 663003469};
    if (mod == 998244353) return {23, 31};
    if (mod == 1004535809) return {21, 582313106};
    if (mod == 1012924417) return {21, 368093570};
    return {-1, -1};
  }
  static constexpr bool can_ntt() { return ntt_info().fi != -1; }
};

#ifdef FASTIO
template <int mod>
void rd(modint<mod> &x) {
  fastio::rd(x.val);
  x.val %= mod;
  // assert(0 <= x.val && x.val < mod);
}
template <int mod>
void wt(modint<mod> x) {
  fastio::wt(x.val);
}
#endif

using modint107 = modint<1000000007>;
using modint998 = modint<998244353>;
#line 1 "string/split.hpp"
vc<string> split(string S, char sep = ',') {
  vc<string> res = {""};
  for (auto&& s: S) {
    if (s == sep)
      res.eb("");
    else
      res.back() += s;
  }
  return res;
}

vc<string> split(string S, string seps = " ,") {
  vc<string> res = {""};
  for (auto&& s: S) {
    if (count(all(seps), s))
      res.eb("");
    else
      res.back() += s;
  }
  return res;
}
#line 3 "linalg/ecottea_matrix_dp.hpp"

// https://maspypy.com/ecottea_dp_estimate_method
// https://atcoder.jp/contests/agc022/tasks/agc022_e
// https://atcoder.jp/contests/toyota2023spring-final/tasks/toyota2023spring_final_f
template <typename mint, int d>
struct ecottea_matrix_dp {
  using MAT = array<array<mint, d>, d>;
  string alphabet;
  map<char, MAT> matrix;
  array<mint, d> X;
  int rank;

  // naive(n): map<string,mint>
  template <typename F>
  void build(string alphabet_, F naive, int max_append = -1) {
    alphabet = alphabet_;
    vc<string> lst;
    if (max_append == -1) max_append = d - 1;
    lst.eb("");
    for (int p = 0; p < len(lst); ++p) {
      string S = lst[p];
      if (max_append < len(S)) break;
      for (auto& a : alphabet) lst.eb(S + a);
    }
    int n = len(lst);

    auto get = [&](string S) -> vc<mint> {
      vc<mint> ANS(n);
      FOR(j, n) ANS[j] = naive(lst[j] + S);
      return ANS;
    };

    vv(mint, mat, d, n);
    vc<string> basis;
    vc<int> pivot(d, infty<int>);
    vv(mint, way, d, d);
    deque<string> que;
    que.eb("");

    auto reduce = [&](vc<mint>& A) -> vc<mint> {
      vc<mint> cf(d);
      FOR(i, len(basis)) {
        mint x = A[pivot[i]];
        FOR(j, pivot[i], n) { A[j] -= x * mat[i][j]; }
        FOR(j, len(basis)) { cf[j] -= x * way[i][j]; }
      }
      return cf;
    };

    while (len(que)) {
      int p = len(basis);
      string S = POP(que);
      vc<mint> row = get(S);
      vc<mint> cf = reduce(row);
      int k = n;
      FOR_R(j, n) if (row[j] != 0) k = j;
      if (k == n) continue;
      if (p == d) {
        print("dim>d");
        assert(0);
      }
      cf[p] += 1;
      mint c = mint(1) / row[k];
      FOR(j, n) row[j] *= c;
      FOR(j, p + 1) cf[j] *= c;
      basis.eb(S);
      pivot[p] = k, mat[p] = row, way[p] = cf;
      ++p;

      auto I = argsort(pivot);
      way = rearrange(way, I);
      mat = rearrange(mat, I);
      pivot = rearrange(pivot, I);
      for (auto& ch : alphabet) {
        que.eb(ch + S);
      }
    }
    rank = len(basis);

    for (auto& ch : alphabet) {
      MAT ans{};
      FOR(i, len(basis)) {
        vc<mint> row = get(ch + basis[i]);
        vc<mint> cf = reduce(row);
        FOR(j, n) assert(row[j] == 0);
        FOR(j, len(cf)) ans[i][j] = -cf[j];
      }
      matrix[ch] = ans;
    }
    FOR(i, len(basis)) X[i] = naive(basis[i]);
    return;
  }

  string to_string(mint x) {
    int a = x.val;
    int b = a - (mint::get_mod());
    return ::to_string(abs(a) <= abs(b) ? a : b);
  }
  string to_string() {
    string out;
    out += alphabet + ".";
    FOR(i, d) out += to_string(X[i].val) + ".";
    for (auto& ch : alphabet) {
      FOR(i, d) FOR(j, d) { out += to_string(matrix[ch][i][j].val) + "."; }
    }
    out.pop_back();
    return out;
  }
  void build_from_string(string S) {
    auto dat = split(S, '.');
    alphabet = dat[0];
    int p = 1;
    FOR(i, d) X[i] = stoi(dat[p++]);
    for (auto& ch : alphabet) {
      FOR(i, d) FOR(j, d) { matrix[ch][i][j] = stoi(dat[p++]); }
    }
  }

  // ? は全部の重ね合わせとして
  mint solve(string S) {
    // 行ベクトルに行列をかけていって第0成分をとる
    MAT ques{};
    for (auto& [ch, A] : matrix) {
      FOR(i, d) FOR(j, d) ques[i][j] += A[i][j];
    }
    matrix['?'] = ques;
    array<mint, d> dp = X;
    for (auto& ch : S) {
      array<mint, d> newdp{};
      assert(matrix.count(ch));
      MAT& mat = matrix[ch];
      FOR(i, d) FOR(j, d) newdp[i] += mat[i][j] * dp[j];
      swap(dp, newdp);
    }
    return dp[0];
  }
};
Back to top page