はじめに
実装メイン.テンプレは別記事.
A
問題文 : https://atcoder.jp/contests/abc426/tasks/abc426_a
リスト [ "Ocelot", "Serval", "Lynx" ] での添字を取得する関数を作っておいて,それぞれ適用して比較します.添字の取得は,高々一個欲しい場合には elemIndex があって,これは Maybe Int を返してくるので fromJust するところまでを関数にしておくと外側がすっきりしてよさげです.
main = do [ s, t ] <- readStrs let f = fromJust . flip elemIndex [ "Ocelot", "Serval", "Lynx" ] putStrLn $ yesno $ f s >= f t
B
問題文 : https://atcoder.jp/contests/abc426/tasks/abc426_b
個数を数えるときに手っ取り早いのは,一旦 sort してしまってから group して連続する同一要素をリストにまとめてしまう方法かなと思っています(いわゆる Run Length Encoding).あとは length の値を見て filter してしまえばほぼお望みのものが手に入ります.この方針だと無理なく一行になりますね.
main = getLine >>= putStrLn . head . filter ( ( == 1 ) . length ) . group . sort
C
問題文 : https://atcoder.jp/contests/abc426/tasks/abc426_c
ユーザ解説にある,順位キューを使う方法を実装します.ただ,Haskell だと標準または近しいところに順位キューは無さそうに見えるので,Data.Set で代用します.が,Data.Set は多重集合ではないので,要素にユニーク ID を添加して擬似的に多重集合にします(真面目にやるなら Data.Map で個数を管理した方がよい気がしますが……).
ということで,$( \text{バージョン}, \text{台数}, \text{ユニーク ID} )$ を要素にした Data.Set と,ユニーク ID として使う値を取り出すための無限リスト [ 1 .. ] をペアにしたものを状態にします.大枠としては,状態と $x, y$ を受け取って,更新後の状態とそのクエリの答えのペアを返す関数を作れば,mapAccumL で畳み込めます.
関数の中身の実装に入ります.Data.Set の先頭側から条件を満たす間取り出し続ける部分でまず迷いますが,takeWhile, dropWhile, span が無い代わりに takeWhileAntitone, dropWhileAntitone, spanAntitone があります.条件が単調である必要がありますが,順位キューとして扱う目的にはむしろ適していて,対数時間で動作します.ということで,( <= x ) . fst3 を条件にして spanAntitone で Data.Set を $2$ つに分けると,前半の snd3 の総和がバージョンアップされる台数で,更新後の情報を表すデータを後半のリストに挿入すれば更新後の状態を作れます.
main = do [ n, q ] <- readInts xys <- replicateM q readInts mapM_ print $ snd $ mapAccumL solve ( Set.fromList ( zip3 [ 1 .. n ] ( repeat 1 ) ( repeat 0 ) ), [ 1 .. ] ) xys solve ( que, (id:ids) ) [ x, y ] = ( ( Set.insert ( y, cnt, id ) s2, ids ), cnt ) where ( s1, s2 ) = Set.spanAntitone ( ( <= x ) . fst3 ) que cnt = sum $ map snd3 $ Set.toList s1
D
問題文 : https://atcoder.jp/contests/abc426/tasks/abc426_d
同じ文字が連続する極大な部分の内,操作せずに残す部分を全通り試すことで最小値を求めます.極大な塊に着目するので,入力を group して塊に分けます.残す塊を固定したとき,塊に含まれない要素は少なくとも $1$ 回ずつ操作され,$2$ 回操作しなければならなない要素の個数は,塊の要素と同じ値の個数です.group した結果に対して,length を map すれば各塊の要素数を,(入力文字列に map digitToInt していたとして,)sum を map すれば $1$ の個数を取り出せます.$0$ の個数はこれらの算数 ($\text{要素数S} - \text{$1$ の個数}$) で求まるので別に計算する必要はありません.
与えられた列の各要素についてそれを含まない部分の和を求められれば「固定した部分以外での寄与」を実装できますが,これは単純に全体から自身を引くだけです(左右に分けて和を取ろうとすると面倒になります($1$ 負)).
main = readInt >>= flip replicateM do n <- readInt as <- map digitToInt <$> readStr print $ solve as solve as = minimum do ( a, both, ones ) <- zip3 ( map head gs ) ( outerSums $ map length gs ) ( outerSums $ map sum gs ) return $ both + if a == 0 then both - ones else ones where gs = group as outerSums bs = map ( sum bs - ) bs