結果

問題 No.2413 Multiple of 99
ユーザー KumaTachiRenKumaTachiRen
提出日時 2023-08-11 23:47:51
言語 C++23
(gcc 12.3.0 + boost 1.83.0)
結果
AC  
実行時間 7,702 ms / 8,000 ms
コード長 8,766 bytes
コンパイル時間 9,311 ms
コンパイル使用メモリ 355,492 KB
実行使用メモリ 110,304 KB
最終ジャッジ日時 2024-04-29 15:04:53
合計ジャッジ時間 75,597 ms
ジャッジサーバーID
(参考情報)
judge4 / judge3
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 23 ms
18,944 KB
testcase_01 AC 24 ms
18,944 KB
testcase_02 AC 40 ms
20,096 KB
testcase_03 AC 24 ms
18,944 KB
testcase_04 AC 5,241 ms
98,812 KB
testcase_05 AC 5,974 ms
100,060 KB
testcase_06 AC 5,974 ms
100,024 KB
testcase_07 AC 7,429 ms
110,304 KB
testcase_08 AC 209 ms
28,916 KB
testcase_09 AC 1,683 ms
37,568 KB
testcase_10 AC 7,464 ms
110,304 KB
testcase_11 AC 7,414 ms
110,180 KB
testcase_12 AC 2,523 ms
40,284 KB
testcase_13 AC 7,422 ms
93,920 KB
testcase_14 AC 7,702 ms
102,624 KB
testcase_15 AC 7,413 ms
103,012 KB
testcase_16 AC 6,752 ms
105,432 KB
testcase_17 AC 6,529 ms
105,180 KB
testcase_18 AC 7,593 ms
109,672 KB
testcase_19 AC 6,290 ms
101,304 KB
testcase_20 AC 2,922 ms
49,216 KB
testcase_21 AC 6,052 ms
84,224 KB
testcase_22 AC 3,304 ms
52,652 KB
testcase_23 AC 1,503 ms
43,944 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

#pragma GCC target("avx2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")

#include <bits/stdc++.h>
#include <atcoder/all>

using namespace std;
using namespace atcoder;

struct Fast {
  Fast() {
    std::cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << setprecision(10);
  }
} fast;

#define popcount(x) __builtin_popcount(x)
#define all(a) (a).begin(), (a).end()
#define contains(a, x) ((a).find(x) != (a).end())
#define rep(i, a, b) for (int i = (a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (a); i--)
#define writejoin(s, a) rep(_i, 0, (a).size()) cout << (a)[_i] << (_i + 1 < (int)(a).size() ? s : "\n");
#define YN(b) cout << ((b) ? "YES" : "NO") << "\n";
#define Yn(b) cout << ((b) ? "Yes" : "No") << "\n";
#define yn(b) cout << ((b) ? "yes" : "no") << "\n";

using ll = long long;
using mint = modint998244353;

template <typename mint>
class Factorial {
 public:
  Factorial(int max) : n(max) {
    f = vector<mint>(n + 1);
    finv = vector<mint>(n + 1);
    f[0] = 1;
    for (int i = 1; i <= n; i++) f[i] = f[i - 1] * i;
    finv[n] = f[n].inv();
    for (int i = n; i > 0; i--) finv[i - 1] = finv[i] * i;
  }
  mint fact(int k) {
    assert(0 <= k && k <= n);
    return f[k];
  }
  mint fact_inv(int k) {
    assert(0 <= k && k <= n);
    return finv[k];
  }
  mint binom(int k, int r) {
    assert(0 <= k && k <= n);
    if (r < 0 || r > k) return 0;
    return f[k] * finv[r] * finv[k - r];
  }
  mint inv(int k) {
    assert(0 < k && k <= n);
    return finv[k] * f[k - 1];
  }

 private:
  int n;
  vector<mint> f, finv;
};

template <typename mint>
struct FormalPowerSeries : vector<mint> {
  using vector<mint>::vector;
  using FPS = FormalPowerSeries;
  FPS &operator+=(const FPS &r) {
    if (r.size() > this->size()) this->resize(r.size());
    for (int i = 0; i < (int)r.size(); i++) (*this)[i] += r[i];
    return *this;
  }
  FPS &operator+=(const mint &r) {
    if (this->empty()) this->resize(1);
    (*this)[0] += r;
    return *this;
  }
  FPS &operator-=(const FPS &r) {
    if (r.size() > this->size()) this->resize(r.size());
    for (int i = 0; i < (int)r.size(); i++) (*this)[i] -= r[i];
    return *this;
  }
  FPS &operator-=(const mint &r) {
    if (this->empty()) this->resize(1);
    (*this)[0] -= r;
    return *this;
  }
  FPS &operator*=(const mint &v) {
    for (int k = 0; k < (int)this->size(); k++) (*this)[k] *= v;
    return *this;
  }
  FPS &operator*=(const FPS &r) {
    auto c = convolution<mint>((*this), r);
    this->resize(c.size());
    for (int i = 0; i < (int)c.size(); i++) (*this)[i] = c[i];
    return *this;
  }
  FPS &operator/=(const FPS &r) {
    if (this->size() < r.size()) {
      this->clear();
      return *this;
    }
    int n = this->size() - r.size() + 1;
    if ((int)r.size() <= 64) {
      FPS f(*this), g(r);
      g.shrink();
      mint coeff = g.at(g.size() - 1).inv();
      for (auto &x : g) x *= coeff;
      int deg = (int)f.size() - (int)g.size() + 1;
      int gs = g.size();
      FPS quo(deg);
      for (int i = deg - 1; i >= 0; i--) {
        quo[i] = f[i + gs - 1];
        for (int j = 0; j < gs; j++) f[i + j] -= quo[i] * g[j];
      }
      *this = quo * coeff;
      this->resize(n, mint(0));
      return *this;
    }
    return *this = ((*this).rev().pre(n) * r.rev().inv(n)).pre(n).rev();
  }
  FPS &operator%=(const FPS &r) {
    *this -= *this / r * r;
    shrink();
    return *this;
  }
  FPS operator+(const FPS &r) const { return FPS(*this) += r; }
  FPS operator+(const mint &v) const { return FPS(*this) += v; }
  FPS operator-(const FPS &r) const { return FPS(*this) -= r; }
  FPS operator-(const mint &v) const { return FPS(*this) -= v; }
  FPS operator*(const FPS &r) const { return FPS(*this) *= r; }
  FPS operator*(const mint &v) const { return FPS(*this) *= v; }
  FPS operator/(const FPS &r) const { return FPS(*this) /= r; }
  FPS operator%(const FPS &r) const { return FPS(*this) %= r; }
  FPS operator-() const {
    FPS ret(this->size());
    for (int i = 0; i < (int)this->size(); i++) ret[i] = -(*this)[i];
    return ret;
  }
  void shrink() {
    while (this->size() && this->back() == mint(0)) this->pop_back();
  }
  FPS rev() const {
    FPS ret(*this);
    reverse(begin(ret), end(ret));
    return ret;
  }
  FPS dot(FPS r) const {
    FPS ret(min(this->size(), r.size()));
    for (int i = 0; i < (int)ret.size(); i++) ret[i] = (*this)[i] * r[i];
    return ret;
  }
  FPS pre(int sz) const {
    return FPS(begin(*this), begin(*this) + min((int)this->size(), sz));
  }
  FPS operator>>(int sz) const {
    if ((int)this->size() <= sz) return {};
    FPS ret(*this);
    ret.erase(ret.begin(), ret.begin() + sz);
    return ret;
  }
  FPS operator<<(int sz) const {
    FPS ret(*this);
    ret.insert(ret.begin(), sz, mint(0));
    return ret;
  }
  FPS diff() const {
    const int n = (int)this->size();
    FPS ret(max(0, n - 1));
    mint one(1), coeff(1);
    for (int i = 1; i < n; i++) {
      ret[i - 1] = (*this)[i] * coeff;
      coeff += one;
    }
    return ret;
  }
  FPS integral() const {
    const int n = (int)this->size();
    FPS ret(n + 1);
    ret[0] = mint(0);
    if (n > 0) ret[1] = mint(1);
    auto mod = mint::get_mod();
    for (int i = 2; i <= n; i++) ret[i] = (-ret[mod % i]) * (mod / i);
    for (int i = 0; i < n; i++) ret[i + 1] *= (*this)[i];
    return ret;
  }
  mint eval(mint x) const {
    mint r = 0, w = 1;
    for (auto &v : *this) r += w * v, w *= x;
    return r;
  }
  FPS log(int deg = -1) const {
    assert((*this)[0] == mint(1));
    if (deg == -1) deg = (int)this->size();
    return (this->diff() * this->inv(deg)).pre(deg - 1).integral();
  }
  FPS pow(int64_t k, int deg = -1) const {
    const int n = (int)this->size();
    if (deg == -1) deg = n;
    if (k == 0) {
      FPS ret(deg);
      if (deg) ret[0] = 1;
      return ret;
    }
    for (int i = 0; i < n; i++) {
      if ((*this)[i] != mint(0)) {
        mint rev = mint(1) / (*this)[i];
        FPS ret = (((*this * rev) >> i).log(deg) * k).exp(deg);
        ret *= (*this)[i].pow(k);
        ret = (ret << (i * k)).pre(deg);
        if ((int)ret.size() < deg) ret.resize(deg, mint(0));
        return ret;
      }
      if (__int128_t(i + 1) * k >= deg) return FPS(deg, mint(0));
    }
    return FPS(deg, mint(0));
  }
  FPS inv(int deg = -1) const {
    assert((*this)[0] != mint(0));
    if (deg == -1) deg = (*this).size();
    FPS ret{mint(1) / (*this)[0]};
    for (int i = 1; i < deg; i <<= 1)
      ret = (ret + ret - ret * ret * (*this).pre(i << 1)).pre(i << 1);
    return ret.pre(deg);
  }
  FPS exp(int deg = -1) const {
    assert((*this)[0] == mint(0));
    if (deg == -1) deg = (*this).size();
    FPS ret{mint(1)};
    for (int i = 1; i < deg; i <<= 1)
      ret = (ret * ((*this).pre(i << 1) - ret.log(i << 1) + 1)).pre(i << 1);
    return ret.pre(deg);
  }
};

vector<mint> mul(const vector<mint> &f, const vector<mint> &g) {
  auto h = convolution(f, g);
  rrep(i, 99, h.size()) h[i - 99] += h[i];
  if ((int)h.size() > 99) h.resize(99);
  return h;
}

using fps = FormalPowerSeries<mint>;

int main() {
  int n, k;
  cin >> n >> k;

  Factorial<mint> fact(2000000);

  int mf = n / 2;
  fps f;
  {
    fps fp = fps(mf * 10 + 1, 0);
    fps fq = fps(mf + 1, 0);
    rep(i, 0, mf + 1) {
      fq[i] = fact.binom(mf, i);
      if (i % 2 == 1) {
        fq[i] = -fq[i];
      }
      fp[i * 10] = fq[i];
    }
    f = fp * fq.inv(mf * 9 + 1);
    f.resize(mf * 9 + 1);
  }
  fps g(f.size());
  rep(i, 0, f.size()) g[i] = f[i];
  if (n & 1) g *= fps(10, 1);
  f.resize(g.size());

  vector<fps> fr(99), gr(99);
  queue<fps> qf, qg, qh;
  rep(r, 0, 99) {
    if (r >= (int)g.size()) {
      fr[r] = fps{};
      gr[r] = fps{};
    } else {
      for (int v = r; v < (int)g.size(); v += 99) {
        qf.push(fps{f[v]});
        qg.push(fps{g[v]});
        qh.push(fps{1, -v});
      }
      while (qf.size() > 1) {
        auto f1 = qf.front();
        qf.pop();
        auto f2 = qf.front();
        qf.pop();
        auto g1 = qg.front();
        qg.pop();
        auto g2 = qg.front();
        qg.pop();
        auto h1 = qh.front();
        qh.pop();
        auto h2 = qh.front();
        qh.pop();
        qf.push(f1 * h2 + f2 * h1);
        qg.push(g1 * h2 + g2 * h1);
        qh.push(h1 * h2);
      }
      auto h = qh.front().inv(k + 1);
      fr[r] = qf.front() * h;
      gr[r] = qg.front() * h;
      qf.pop();
      qg.pop();
      qh.pop();
    }
  }

  mint ans = 0;
  rep(v, 0, 99) {
    int w = (99 - v) * 10 % 99;
    auto frr = fr[v];
    auto grr = gr[w];
    rep(i, 0, fr[v].size()) {
      int j = k - i;
      if (j < (int)gr[w].size()) ans += fact.binom(k, i) * frr[i] * grr[j];
    }
  }
  cout << ans.val() << "\n";
}
0