Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

ABC063をHaskellで

Posted at

A - Restricted

問題 ABC063A

シグネチャを決める。

abc063a :: Int -- A
        -> Int -- B
        -> String -- 答え

結果

abc063a [a, b]
  | ans >= 10 = "error"
  | otherwise = show ans
  where
    ans = a + b

B - Varied

問題 ABC063B

シグネチャを決める。

abc063b :: String -- S
        -> Bool   -- 答え

それぞれの文字の出現回数を数えて、1を超えるものがあったらアウト。

リストベース

import Data.List

abc063b :: String -> Bool
abc063b = all isSingleton . group . sort
  where
    isSingleton [_] = True
    isSingleton _   = False

配列ベース

import Data.Array.Unboxed

abc063b :: String -> Bool
abc063b s = all (1 >=) $ elems (accumArray (+) 0 ('a','z') [(c,1) | c <- s] :: UArray Char Int)

文字コードとビット操作ベース

配列の代わりにビットを使い、衝突が最初に見つかったところで終了する。

import Data.Bits

abc063b :: String -> Bool
abc063b s = loop (0 :: Int) s
  where
    loop _ [] = True
    loop b (c:cs) = b /= b1 && loop b1 cs
      where
        b1 = setBit b $ fromEnum c

総当たりで文字が異なることを調べる

公式解説の最初の方針

import Data.List

abc063b :: String -> Bool
abc063b s = and [c /= d | c:ds <- tails s, d <- ds]

C - Bugged

問題 ABC063C

シグネチャを決める。

abc063c :: Int   -- N
        -> [Int] -- s_i
        -> Int   -- 答え

引っかかって真面目にDP

可能な点数の組み合わせは $2^{100}$ とおりはなく、$0 ~ 100 \times 100$ の範囲しかないので、
それを全て見つけた上で、10の倍数でない最大値か、どれもなければ0を答える。

import Data.Array.Unboxed
import Data.List

abc063c :: Int -> [Int] -> Int
abc063c n ss =
  | null cands = 0
  | otherwise  = head cands
  where
    ub = 100 * n
    arr0 = listArray (0, ub) $ True : repeat False :: UArray Int Bool
    arrN = foldl' step arr0 ss
    step arr s = accum (flip const) arr [(t + s, True) | (t, True) <- assocs arr]
    cands = [p | p <- [ub, pred ub .. 1], mod p 10 /= 0, arrN ! p]

もっと賢く、直接求める

満点が10の倍数でなければそれが自明に最大。
10の倍数であるとき、いずれか1問をわざと失点することで表示されることを目指す。
そうするとき、配点が最小なものの方がよいが、配点が10の倍数では意味がないので、そうでないものから選ぶ。
しかしそういう候補がないときは、どうやっても0点。

abc063c n ss
  | isOK total = total -- 真の最大値がそのまま使えるならそれが答え
  | null oks   = 0     -- 配点が全て10の倍数だと、どう失点しても10の倍数から逃れられない
  | otherwise  = total - minimum oks -- 最小限の失点で10の倍数でなくする
  where
    total = sum ss
    isOK x = mod x 10 /= 0
    oks = filter isOK ss

D - Widespread

問題 ABC063D

シグネチャを決める。

abc063d :: Int   -- N
        -> Int   -- A
        -> Int   -- B
        -> [Int] -- h_i
        -> Int   -- 答え

シミュレーションしていては間に合わない。

一度の攻撃で全ての敵に B のダメージは与えることができ、狙った一体だけは追加で $D = A - B$ を与えられる。
K 回の攻撃で終わらせられたとすると、

  • 全員にそれぞれ $KB$ のダメージを与える。弱い敵はその巻き添えだけで倒れる。
  • これでは足らない敵それぞれについて、全部で K 回狙うことができる。
    これで足りるとは、$\lceil (h_i - KB) / D\rceil$ の総計が K 以下ということ。

この条件を満たす最小のKを二分探索で探す。

結果

import qualified Data.Vector.Unboxed as UV

abc063d :: Int -> Int -> Int -> [Int] -> Int
abc063d n a b hs = snd $ binarySearch prop 0 (10^9 + 1)
  where
    hv = UV.fromListN n hs
    d = a - b
    prop k = k >= UV.foldl' f 0 hv
      where
        kb = k * b
        f acc h = acc + divrup (max 0 $ h - kb) d

divrup :: Int -> Int -> Int
divrup x y = negate $ div (negate x) y

binarySearch :: (Int -> Bool) -> Int -> Int -> (Int, Int)
binarySearch prop unsat sat = loop unsat sat
 where
   loop a b
     | ende   = (a, b)
     | prop m = loop a m
     | True   = loop m b
     where
       ende = a == m || b == m
       m = div (a + b) 2
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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?