AtCoder部 2026-01-14
久しぶりに Haskell で解いてみました。慣れなくて時間がかかりました。
ABC426 D - Pop and Insert
全て0にするときの最小コストを考える(0と1を反転して解けば1にするときの最小コストも求められるので、0にするときだけ考えればok)。
全ての1を、ある位置より左にあるグループと右にあるグループに分けて、左にあるグループは左からの操作で、右にあるグループは右からの操作で0にすることにする。 このときのコストは、1の個数+無駄に操作しないといけない0の個数×2となる。 分け方を全て試して、最小値を求めればよい。
solve' :: Int -> [Char] -> Int
solve' n s =
let countZero acc c = acc + if c == '0' then 1 else 0
ones = 0 : (map (+ 1) . elemIndices '1') s ++ [n + 1]
cumL = IA.listArray @UArray (0, n) $ scanl' countZero (0 :: Int) s
cumR = IA.listArray @UArray (1, n + 1) $ scanr (flip countZero) (0 :: Int) s
in minimum $ zipWith (\cur nxt -> cur + (n + 1 - nxt) + cumL IA.! cur + cumR IA.! nxt) ones (drop 1 ones)
solve :: IO Int
solve = do
n <- readInt <$> BS.getLine
s <- BS.unpack <$> BS.getLine
let rev c = if c == '0' then '1' else '0'
return $ min (solve' n s) (solve' n (map rev s))
main :: IO ()
main = do
t <- readInt <$> BS.getLine
ans <- replicateM t solve
putStr $ unlines $ map show ans
ABC427 C - Bipartize
頂点を2つのグループに分ける方法を全て試せばok。
main :: IO ()
main = do
(n, m) <- readInt2 <$> BS.getLine
es <- replicateM m (readInt2 <$> BS.getLine)
let ans = minimum $ map (f . IA.listArray @UArray (1, n)) $ replicateM n [0 :: Int, 1 :: Int] :: Int
where
f colors = sum $ map (\(u, v) -> if colors IA.! u == colors IA.! v then 1 else 0) es
print ans
ABC428 C - Brackets Stack Query
「正しいカッコ列である」は「( を +1、) を -1 として累積和を取ったとき、常に0以上かつ末尾が0」と言い換えられる。
(累積和の列, 負の要素の個数)を状態として、クエリごとに適切に状態遷移させていけばよい。
main :: IO ()
main = do
q <- readInt <$> BS.getLine
qs <- replicateM q (words . BS.unpack <$> BS.getLine)
let ans = drop 1 $ scanl' f ([0], 0) qs
where
f (x : xs, cnt) query = case query of
["1", c] ->
let acc = x + if c == "(" then 1 else -1
cnt' = cnt + if acc < 0 then 1 else 0
in (acc : x : xs, cnt')
["2"] ->
let cnt' = cnt - if x < 0 then 1 else 0
in (xs, cnt')
putStr <$> unlines $ map (yesno . \(x : xs, cnt) -> x == 0 && cnt == 0) ans