3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AtCoder Beginner Contest 424 振り返り

Posted at

AtCoder Rated参加です

結果

今回はABの2完でした。

スクリーンショット 2025-09-21 11.17.12.png

スクリーンショット 2025-09-21 11.16.30.png

ふりかえり

A問題

二等辺三角形なので、
a == b || b == c || c == a

が成り立てばよいです。

main :: IO ()
main = do
  [a, b, c] <- getInts

  printYn $ a == b || b == c || c == a

B問題

同じイベントが 2 回以上起きることはありません。と書いてあるのがポイントで、一人の人が同じ問題を複数解くことはありません。なので、

『どの問題を解いたか?』

は重要でなく

『幾つ問題を解いたか?』

が重要になります。

よって、人ごとに問題を解いた回数をIntMapとして持っておいて、イベントをこなす度に解いた問題数をインクリメントし、カウント数が問題の総数と同じであれば解答者のリストに追加していけば良いです。

main :: IO ()
main = do
  [n, m, k] <- getInts
  abs <- replicateM k getInts

  let im = IM.fromList $ map (,0) [1 .. n]
      (_, ans) =
        foldl'
          ( \(im1, ans) [a, b] ->
              let im2 = IM.adjust succ a im1
                  cnt = fromJust $ IM.lookup a im2
               in bool (im2, ans) (im2, a : ans) (cnt == m)
          )
          (im, [])
          abs

  printList $ reverse ans

C問題(upsolved)

i番目が(0, 0)だった時、(a, b)のいずれかがiと同じであればその問題が次の解ける問題になります。これを繰り返していって解いた問題の数をカウントする問題です。

最初は時系列順に上から問題を解いていくと勘違いしていたので、Setで次の解ける問題を保持し、foldl'で回していました。

その回答が以下になります。
https://atcoder.jp/contests/abc424/submissions/69484808

main :: IO ()
main = do
  n <- readLn @Int
  abs <- zip [1 ..] <$> replicateM n getInts

  let ans =
        foldl'
          ( \s (i, [a, b]) ->
              if a `IS.member` s || b `IS.member` s || (a == 0 && b == 0)
                then IS.insert i s
                else s
          )
          IS.empty
          abs

  print $ IS.size ans

この回答の場合、サンプルはパスしますが、提出時にWAとなってしまいます。

実際は時系列順に解くのではなく、(0, 0)となる問題から出発し、次の解ける問題へと繋げていくグラフのような問題となります。

たとえば、この解法だと以下のような入力だとWAになります。

3
0 0
4 3
1 2

この場合、問題を解く順番は以下のようになります

スキル習得順序: 1 → 3 → 2

    ┌─────────────────┐
    │                 ↓
    ①       ②       ③
             ↑────────┘

習得の流れ:
①スキル1 (初期習得済み)
   ↓
③スキル3 (スキル1があるので習得可能)
   ↓  
②スキル2 (スキル3があるので習得可能)

このような入力でもPassするためには、bfsもしくはdfsで解く必要があります。さらに、始点となる(0, 0)は複数あるので、実際解く時は複数始点となります。

コンテスト中にここまで気づくことができたのですが、時間内に複数始点bfs, dfsを書けずに終わってしまいました。

それぞれの回答を載せておきます。(ちなみに、私の持っているimmutable版だとTLEになりました。)

解答(bfs版)

main :: IO ()
main = do
  n <- readLn @Int
  abs <- zip [1 ..] <$> replicateM n getInts

  let init = [i | (i, [a, b]) <- abs, a == 0, b == 0]
      g = buildG (1, n) $ concat [[[a, i], [b, i]] | (i, [a, b]) <- abs, a /= 0, b /= 0]
      ans = bfsRunSTUArray n (g !) init

  print $ length $ findArrayIndices id ans

bfsRunSTUArray :: Int -> (Int -> [Int]) -> [Int] -> UArray Int Bool
bfsRunSTUArray n getNext initNodes = runSTUArray $ do
  visited <- newArray (1, n) False
  queueRef <- newSTRef initNodes

  let loop = do
        queue <- readSTRef queueRef
        case queue of
          [] -> return visited
          currentLevel -> do
            writeSTRef queueRef []
            nextLevel <- foldM processNode [] currentLevel
            if null nextLevel
              then return visited
              else do
                writeSTRef queueRef nextLevel
                loop

      processNode acc curr = do
        isVisited <- readArray visited curr
        if isVisited
          then return acc
          else do
            writeArray visited curr True
            let neighbors = getNext curr
            return (neighbors ++ acc)
  loop  

解答(dfs版)

main :: IO ()
main = do
  n <- readLn @Int
  abs <- zip [1 ..] <$> replicateM n getInts

  let init = [i | (i, [a, b]) <- abs, a == 0, b == 0]
      g = buildG (1, n) $ concat [[[a, i], [b, i]] | (i, [a, b]) <- abs, a /= 0, b /= 0]
      ans = dfsRunSTUArray n (g !) init

  print $ length $ findArrayIndices id ans

dfsRunSTUArray :: Int -> (Int -> [Int]) -> [Int] -> UArray Int Bool
dfsRunSTUArray n getNext initNodes = runSTUArray $ do
  visited <- newArray (1, n) False
  stackRef <- newSTRef initNodes

  let loop = do
        stack <- readSTRef stackRef
        case stack of
          [] -> return visited
          (curr : rest) -> do
            writeSTRef stackRef rest
            isVisited <- readArray visited curr
            if isVisited
              then loop
              else do
                writeArray visited curr True
                let neighbors = getNext curr
                modifySTRef' stackRef (neighbors ++)
                loop
  loop

全体を振り返って

解法に気づくことはできたのですが、時間内に解くことができませんでした。
手持ちのbfs, dfs強化したので、次はもっと楽に解けるようになると思います。

とはいえ単純に慣れが足りないので、まずはdfs, bfsの問題を多めに解くことから始めていきます。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?