AOJ3336 Fish Tank

備忘録。想定解をそのまま追っかけただけの記事なので新規性は無いです。

問題概要

https://onlinejudge.u-aizu.ac.jp/problems/3336

長さ nの広義単調減少列 Aが与えられる。以下の操作を何回か行うことを考える。

  • 添字 iを一つ選んで、 A_{i} 1増やす。

各操作の前後で Aは広義単調減少でなければならない。全ての A_{i} Mにする操作の仕方の通り数を 998244353で割ったあまりを求めよ。

制約

  •  1\ \le\ N\times M\ \le\ 200000
  •  1\ \le\ A_{i}\ \le\ M
  •  Aは広義単調減少

解法

https://drive.google.com/drive/folders/1KoV507hNY39alt9nXGOu0OjRS2tEyH2b

  • 想定解はM.pdfに書かれている。

ヤング図形で考える。操作はいずれかの行を選び、右端に正方形を一個追加することと同じ。操作後もヤング図形で無いといけない。

・・・こっち側もヤング図形だな。

条件を満たしながら目的を達成する操作を手順を一つ取る。置いた正方形の順番をヤング図形に書き込む。順番の降順に数を書くと、標準盤になっていることがわかる。

反対に標準盤を一つ取って、書かれた数の降順に操作を行う(正方形を置く)と条件を満たしながら目的を達成することができる。

よって標準盤の数を数えれば良いことがわかる。標準盤の数についてはフック長の公式というものがあって、この公式を素直に適用すると  O(NM + \log \text{MOD})で数え上げることができる。

using mint = atcoder::modint998244353;

int main() {
    SetFastIO();

    int n, m; std::cin >> n >> m;
    std::vector<int> a(n);
    int sum{};
    for (int i{n - 1} ; i >= 0 ; i--) {
        std::cin >> a[i];
        a[i] = m - a[i];
        sum += a[i];
    }

    mint num{1};
    for (int i{1} ; i <= sum ; i++) num *= mint::raw(i);
    mint den{1};
    for (int i{} ; i < n ; i++) {
        int dn{n - 1};
        for (int j{} ; j < a[i] ; j++) {
            while (j >=  a[dn]) dn--;
            // std::cerr << i << ' ' << j << ' ' << dn << ' ' << a[i] - j + dn - i << std::endl;
            den *= mint::raw(a[i] - j + dn - i);
        }
    }

    mint ans{num / den};
    std::cout << ans.val() << '\n';
}

感想

知らないと詰んでしまうタイプのテクニック、どっかにまとめておきたい。