はじめに
テンプレは別記事.
A
問題文 : https://atcoder.jp/contests/abc403/tasks/abc403_a
リストの奇数番目の要素の総和を求める問題です.奇数番目の要素を抜き出すのは地味に面倒そうですが,面白いイディオムがあります.まず,cycle [ True, False ] で $\mathrm{True}$ と $\mathrm{False}$ が交互に出現する無限リストを作れます.このリストを filter id すると(ある種の Toy Problem ですが)奇数番目の要素だけを取り出していることになります.よって,このリストを入力のリストと zip して filter することで,入力のリストの奇数番目の要素だけを取り出すことができます.
あとは map で入力のリストに対応する方を取り出して sum すれば答えが求まります.
main = getLine >> readInts >>= print . sum . map snd . filter fst . zip ( cycle [ True, False ] )
B
問題文 : https://atcoder.jp/contests/abc403/tasks/abc403_b
入力が小さく $T$ から部分文字列を切り出す開始位置を全て試しても間に合うので,そのようにします.始点を全て試すのは tails でできますが,長さが $|U|$ 未満のものを取ってしまうと後々困るので,$|S| - |U| + 1$ 個を take します.
$T$ の部分文字列の $i$ 番目の文字が $c_1$ で $U$ の $i$ 番目の文字が $c_2$ だったとき.$c_1 = \text{'?'}$ または $c_1 = c_2$ のときマッチさせることができます.従って,この条件で zipWith して and することで,この部分で全てマッチさせられるかを判定できます(長さ $|U|$ 未満の部分文字列を取ってしまうとここでミスる).
あとは $T$ の各部分文字列について上記の判定をして or すれば問題を解けます.
main = do t <- getLine u <- getLine putStrLn $ yesno $ or $ do s <- take ( length t - length u + 1 ) $ tails t return $ and $ zipWith f s u f '?' _ = True f c1 c2 = c1 == c2
C
問題文 : https://atcoder.jp/contests/abc403/tasks/abc403_c
$\text{ ヒト } \times \text{ コンテスト } \rightarrow \{ \mathrm{ True }, \mathrm{ False } \}$ な写像を二次元配列で表すと MLE するので,別の方法で情報をもちます.まずヒト $i$ がコンテスト $j$ を読める状態を順序対 $( i, j )$ で表現することにします.クエリ $1$ で与えられる情報はこれで表現できます.クエリ $2$ を同じ方法で表現しようとするとデータの数が多くなってしまって TLE/MLE するので,ヒト $i$ が全てのコンテストを読める状態を順序対 $( i, 0 )$ で表現することにします.これらの情報を Data.Set で管理します.
こうすると,クエリ $3$ に対しては $( x, y )$ または $( x, 0 )$ が Set 内にあるかどうかを調べることで全てのクエリを $O( \log q )$ 時間で処理できます.
実装としては,Set とクエリを受け取って,そのクエリを処理した後の Set と出力値(に対応する Bool)を返すような関数を作ると mapAccumL で畳み込むことができます.ただし,出力はクエリ $3$ にしか無いので,クエリ $1, 2$ に対する出力は Nothing で表し,クエリ $3$ の出力に対応する真偽値を Just に入れて表現します.呼び出し側では.catMaybes で Just 値だけ集めてから文字列に変換する関数を map することで望むものが得られます.
main = readInts >>= flip replicateM readInts . last >>= mapM_ putStrLn . map yesno . catMaybes . snd . mapAccumL solve Set.empty solve set [ 1, x, y ] = ( Set.insert ( x, y ) set, Nothing ) solve set [ 2, x ] = ( Set.insert ( x, 0 ) set, Nothing ) solve set [ 3, x, y ] = ( set, Just $ Set.member ( x, 0 ) set || Set.member ( x, y ) set )
D
問題文 : https://atcoder.jp/contests/abc403/tasks/abc403_d
$d = 0$ の場合は $A$ から重複無く要素を最大限取り出せばよいので,$n$ から $A$ のユニーク要素数を引いた値が答えになります.ユニーク要素数は,素朴に(かつ一般的に)やるなら sort + group でユニーク要素を一つのリストに固めた二重リストを作れるので,更に length をとると求まります.今回の場合は値が Int なので,一旦 Data.IntSet に全部突っ込んでから size をとることで定数倍速い*1実装になります.
$d \neq 0$ のケースについて考えます.この場合は $d$ での剰余が異なる値同士は干渉しないので,剰余毎に独立に最適値を求めた和が答えです.剰余が等しい値を昇順に並べることを考えると,この列挙において連続する値を共に残すことはできません.このコンセプトに従って残すか否かを昇順に選ぶと考えると,
\[
\mathit{ dp }_{ i, j } = \text{$i$ 個分の扱いを決定済み.次の要素について,$j = 1$ iff 次の値は消さなければならない}
\]
という風に状態を取る DP が設計できます.DP はかなりミュータブルなアルゴリズムなので,DP テーブルは ST モナドの中で STUArray を使います.
DP の中で各値を残さないことにした場合の答えへの寄与は,その値の $A$ での出現回数です.個数を数えるには,accumArray (+) 0 で一旦配列にしてから elems でリストに戻すと線形時間で処理できます.$d$ での剰余毎に分類するには,一旦 Data.List.Extra にある chunksOf d で $d$ 個毎に区切ってから transpose で転置すればよいです.
main = do [ n, d ] <- readInts as <- readInts let cs = transpose $ chunksOf d $ elems $ accumArray (+) 0 ( 0, 10^6 ) $ zip as ( repeat 1 ) print $ if d == 0 then n - ISet.size ( ISet.fromList as ) else sum $ map solve cs solve cs = runST $ do dp <- newArray ( ( 0, 0 ), ( n, 1 ) ) ( maxBound `div` 2 ) :: ST s ( STUArray s ( Int, Int ) Int ) -- ( # of considered, force to remove next ) -> min cost writeArray dp ( 0, 0 ) 0 writeArray dp ( 0, 1 ) 0 forM_ ( zip [ 0 .. ] cs ) $ \( i, c ) -> do forM_ [ 0, 1 ] $ \j -> do modifyArray dp ( i + 1, 0 ) =<< min . ( + c ) <$> readArray dp ( i, j ) when ( j == 0 ) $ do modifyArray dp ( i + 1, 1 ) =<< min <$> readArray dp ( i, j ) min <$> readArray dp ( n, 0 ) <*> readArray dp ( n, 1 ) where n = length cs
*1:少なくとも今回の問題への提出結果によれば.