プログラミングを上達させたい

情報学専攻の大学院→放送局でCMの営業など@大阪→舞台俳優&IT営業@東京

AtCoder Beginner Contest 205に出ました

少し間が空きましたが、Ratedなコンテストに出ました。
atcoder.jp
典型90問を少しずつ解いていますが、今はまだ「自分のレベルで考えたら解けるもの」しか解いていません(といっても数十分考えたりはする)。
よって、ややこしく大きめなコードを書くスピードは少し上がったものの、解ける問題が本質的に増えたかというと微妙な感じです。
そんななか向かえたBeginner Contest。どうなるのか。
速く解くしかない。

→結果、4完(33分)でした。A〜D解いたあとにE問題に取り組むもオーダーを落とす方法が分からず・・・悔しい。
というわけで、コードおよび考え方を書いていきます。
全てScheme(racket)で解きました。最近、2次元配列が出てこない限りはSchemeが一番書きやすいという状態です。

A問題

かけ算と割り算をして、ちゃんと小数で出しましょうという問題。
普通に / で割り算するとSchemeの場合は分数になってしまうので、exact->inexact で小数に変換します。

#lang racket
(define A (read))
(define B (read))
(display (exact->inexact (* B (/ A 100))))
(newline)

B問題

N個の数からなる数列の中に、1〜Nが1個ずつ入ってるか確認する問題。

#lang racket
(define N (read))
(define NUMS (make-vector N 0))
(define (readN n)
  (if (= n 0) '()
      (let ((ele (- (read) 1)))
        (cons ele (readN (- n 1))))))
(define (update numlist)
  (if (null? numlist) #t
      (let* ((nownum (car numlist))
            (nowcount (vector-ref NUMS nownum)))
        (if (> nowcount 0) #f
            (begin
              (vector-set! NUMS nownum 1)
              (update (cdr numlist)))))))
(display (if (update (readN N)) "Yes" "No"))
(newline)

C問題

Cが偶数の場合は絶対値で、Cが奇数の場合はA,Bの大小をそのままでOKな問題。
ビビってCが奇数のときはなぜか A^3 と B^3を比較するコードにしました。コーナーケースありそうすぎて怖かったです。

#lang racket
(define A (read))
(define B (read))
(define C (read))
(display
 (let* ((absA (abs A))
        (absB (abs B))
        (A3 (* A A A))
        (B3 (* B B B)))
   (if (= 0 (modulo C 2));C=even
       (cond ((> absA absB) ">")
             ((< absA absB) "<")
             (#t "="))
       (cond ((> A3 B3) ">")
             ((< A3 B3) "<")
             (#t "=")))))
(newline)

D問題

数列の長さは10^5まであるものの、「数列に出現しないうちK番目の数」を出せというKは10^18までという問題。
数列に出現しない数たちを、数列の長さにのみ依存する形で保持したいです。
ここで、("出現しない数A" "Aから何個分連続で出現しないか" "これまで見てきた出現しない数の合計")という数字3つ組でデータを保持することを考えます。
つまり
数列→(4,6)
出現しない数の列→(1,2,3,5,7,8,9,10,,,,,,,)
保持の仕方→( (1 3 3) (5 1 4) (7 大きな数 大きな数) )
ということです。
このように保持し、i番目のものを求めるときには数字3つ組の3番目に注目して二分探索を行うことで、Q * log N で計算できます。

#lang racket
(define N (read))
(define Q (read))
(define (readN n)
  (if (= n 0) '()
      (let ((ele (read)))
        (cons ele (readN (- n 1))))))
(define INP (list->vector (readN N)))
(define QUERY (readN Q))
 
(define (make-nouse-list)
  (define (sub start sum index)
    (if (>= index N) (list (list start 1000000000000000000 200000000000000000))
        (let ((usenum (vector-ref INP index)))
          (if (= start usenum)
              (sub (+ start 1) sum (+ index 1))
              (cons (list start (- usenum start) (+ sum (- usenum start)))
                    (sub (+ usenum 1) (+ sum (- usenum start)) (+ index 1)))))))
  (sub 1 0 0))
(define NOUSES (list->vector (cons (list 0 0 0) (make-nouse-list))));(display NOUSES)
(define NOUSESLEN (vector-length NOUSES))
(define (search ith)
  (define (sub left right)
    (if (= (- right left) 1)
        (let ((before-sum (third (vector-ref NOUSES left)))
              (newstart (car (vector-ref NOUSES right))))
          (+ newstart (- ith (+ before-sum 1))))
        (let* ((center (quotient (+ left right) 2))
               (centersum (third (vector-ref NOUSES center))))
          (if (<= ith centersum)
              (sub left center)
              (sub center right)))))
  (sub 0 (- NOUSESLEN 1)))
(define (solve q)
  (if (null? q) (display "")
      (begin
        (display (search (car q)))
        (newline)
        (solve (cdr q)))))
 
(solve QUERY)

4問目まではそこそこなスピードで解けましたが、果たしてパフォーマンスはどれくらいか・・・
あと、5問目の解説を見ると、上手な考察ができればスパッと解けていたようで、とても悔しいです・・・(といってもそれを思いつくのが簡単なわけではないですが)

解ける問題を増やすべく、典型90問の★5を一旦クリアするのを目標にします。
オーディションやらなんやらもあるけど、なんとか時間確保して頑張るぞー