A06 - How Many Guests?¶
- created: 2022-12-27 tue
- ご意見・ご要望はissue・プルリク用のGitHubまで
- 競技プログラミングのためのF#入門
- GitHub上の対応ディレクトリ
- 公式ページ
- 要点: アルゴリズム, ループを減らす
入出力¶
1 2 3 4 | |
方針¶
都度入場者数を計算していたら間に合わないため先に計算します. あとの処理を綺麗に書くための計算法もポイントです.
解説¶
i日目までの入場者数の計算¶
累積的な計算が必要です. もちろんAiから素直に計算すれば問題ありません. 例えば次のように書けます.
1 2 3 4 | |
forで破壊的に書きました. もう少し関数型らしく書きたければ次のようにfoldで書けます.
1 2 3 4 5 | |
ここでNa0.[i] <- Aa.[i]などはunitを返すだけで配列を返さないため, 明示的に配列Na0を返す必要があります.
少し余談を挟みます. ちなみにfoldの内部で破壊的な計算をしています. しかしこれはfoldの外部には漏れません. 関数プログラミングでもパフォーマンスを求める場合は破壊的な処理を使いますし, 破壊的な操作はできる限り外に漏れないようにします. いまは必ずしもパフォーマンスを求める場面ではないものの, F#の配列に対する仕様によって破壊的な処理が便利な部分です. Haskellでは明示的にモナド環境下で作業するように強制されます.
さて, これを使って計算しても構いません. ただ後の処理がやや面倒になるため, 次のようにscanを使って累積和を計算しましょう.
1 | |
これと先のNa0が何が違うかは実行するとわかります. 問題で出てきた入力例で確認しましょう.
1 2 3 4 5 6 7 8 9 10 11 12 | |
後者は先頭に0がはさまっています. (0,Aa)とした0です. 今回はこれを使いますが, もし先頭の0がほしくない場合は例えば次のように書くといいでしょう.
1 2 | |
クエリへの対応¶
先のNaを使えば素直に計算できます.
1 2 | |
ここでのポイントはNa.[r] - Na.[l-1]です. Liには1が来る場合もあり, この場合はRi日目までの全ての入場者を計算する必要があります. 特にNa.[l-1]の減算なしでNa.[r]だけを返す必要があって条件分岐が入ります. さらに条件分岐で適切な対処をしなければ, Na.[l-1]はl-1 = -1に対する配列外参照の実行時エラーまで起こり得ます. これが先のscanでの初項0の意義です.
コードがわかりにくくなるためわざわざ書きませんが, scanによる初項0がなくてももちろん正しいコードは書けます.