ymduu+2

初心を忘れないのでツンデレとPSPが今でも好き、技術メモとか制作物とかそういうの

キーエンス プログラミング コンテスト 2019 D Double Landscape

所見

方針は外していなかった気がするけど微妙に間違っていた、こういうのがこれ(下)なんだと思う、くやC
ところで私はmdをプラグイン入れたVSCodeで書いてるんですが(プレビューを横で見たいので)、はてなブログに貼り付けた時に勝手にURL展開されてほしくないですか?

問題概要

長さN,Mの列A,Bが与えられる。 N行M列のグリッドに1からN*Mまでの整数を重複なく書き込むとき、 - i行目に書き込まれている値のうち,最大の値はAi - j行目に書き込まれている値のうち,最大の値はBi
となるような書き込み方はいくつあるか。1000000007で割ったあまりを求めよ。

取った方針

要するにi行目には必ずAiが含まれることを忘れており死亡

正しい方針

制約の厳しい大きい数から埋めていく。(貪欲をするときは制約が厳しい順に埋めていくテク)
i行目にはAiが、j列目にはBjが必ず含まれることに気を付ける。
- Ai == x && Bj == xのときxはi行j列に配置される
- Ai == x 、Bにxは含まれないときxはi行目のうちBj > x なる場所に配置される。Bにのみxが含まれる場合も同様(等号が含まれないのはAi != Bjで考えているため)
- AにもBにもxが含まれないとき、 Ai > x && Bj > xなる場所に配置される。このようなi, jの個数はxが置ける行 or 列を累積和っぽく持つことで高速に求めることができる(事前計算O(NM)、クエリO(1))

ここで、大きい数から埋めていったとき、Aにしか含まれない数A[k] == yを配置しようとした時にはA行目がすべて埋まってしまっており配置できないのでは、という疑問が発生する。
しかし、これは以下を考えることでありえないとわかる。
理由: 大きい数から埋めていくので、A[k] == yなる数を配置するときはその数がk行目に最初に入る数である。
考えてみると自明なんだけど、ちょっと迷うと時間が消えるのでキャッシュしておきたい

得られた知見

  • 問題を誤読しない
  • 貪欲をするときには制約が厳しい順に埋めていくテク
  • 数列Aが与えられたとき、その要素よりも小さい(大きい)数は数列内にいくつあるか、というのは累積和に似た考え方で高速に求められる。

解くのに必要な要素

  • 貪欲をするときには制約が厳しい順に埋めていくテク
  • 累積和
  • 場合の数
  • i行目には必ずAiが含まれることを忘れない注意力

周辺調査によって得られた知見

  • C++における配列のソートはsort(a, a+N)(Nは要素数) でできる(今更)
    • 入力を取るときvectorに取るよりもタイプ数が少ない
  • 最近諸事情でエゲツないマクロに慣れ親しんでいるので、そろそろ闇魔法に手を出してもいいのでは?