HaskellでProjectEuler+やってみた

ProjectEuler+ is 何

Project Eulerの問題を競技プログラミング風の入力形式で出題し、ジャッジしてくれる。 HackerRank上で行われているコンテスト形式ではあるが、"Open Indefinitely"とあり、いつでも挑戦できる。

今回はとりあえず#1に挑戦。

問題概要

T個のNに対して、N未満の3または5の倍数になっている数字の総和を求めよ。

とりあえず愚直に

import Control.Monad

solve :: Int -> Int
solve n = sum [x | x <- [1..n-1], mod x 3 == 0 || mod x 5 == 0]

main = do
  t <- readLn
  n <- replicateM t readLn
  mapM print $ map solve n

実行して見るが、"Terminated due to timeout"ではじかれる。
どうやら制限時間は5秒に設定されているらしい・・・

とりあえず時間がかかってそうなsumを正格版に書き直してみる。

solve n = foldl' (+) [x | x <- [1..n-1], mod x 3 == 0 || mod x 5 == 0]

が、結果変わらず。。。

制約を見てみる

1 ≦ T ≦ 105
1 ≦ N ≦ 109

・・・・・・えっ
最大ケースってどんな速い言語でも無理なんじゃ・・・?

解法を見直し

3または5の倍数のふるいをかける部分をできるだけ小さくする。

solve n = s + t + sum [x | x <- [p*15+1..m], mod x 3 == 0 || mod x 5 == 0]
  where
    p = div m 15
    q = mod m 15
    m = n - 1
    s = p * sum l
    t = sum [0..p-1] * 15 * length l
    l = [x | x <- [1..15], mod x 3 == 0 || mod x 5 == 0]

これでいけると思いきや、まだ"Terminated due to timeout"の判定。
どうしようもないのでsumの部分を計算してマジックナンバーにした。

solve n = s + t + sum [x | x <- [p*15+1..m], mod x 3 == 0 || mod x 5 == 0]
  where
    p = div m 15
    q = mod m 15
    m = n - 1
    s = p * 60
    t = u * 15 * 7
    u = div (p * (p-1)) 2

これで何とかSuccess。むずかった・・・