LoginSignup
1
0

More than 1 year has passed since last update.

ABC271-C (シンプル)

Posted at


(要約)
$k$巻まで読むと仮定し、$k$巻以下で重複する本は売却します。
売らずに読む本をs冊とすると残りはn-s冊で、それらを全部売って$k$巻まで揃うかどうかをみればよいです。
(注意)
Julia 1.4です。知らなくても理解できるとは思います。

考察

  • 2冊の本を売って1冊の本を買うと、本の総冊数は減少する。なのでなるべく売買しないほうがよい
    (まず全部売ってしまってから考えるというアプローチは不可)

  • $a_i$ の上限が大きい($10^9$)ので対策が必要だが、本の総冊数は最大で$3×10^5$なので解はこの値を超えることはない。
    (上界を見積もる)

  • 売るなら読まれる可能性が最も小さい巻、つまり最後の巻が優先
    (貪欲法)

  • 巻が重複している可能性がある
    (コーナーケースというほどではないが。サンプルコードみて初めて気づいた)

アプローチ

pseudocode
k = 0
while true
    k += 1
    if (k巻まで読める)
        continue
    else
        print(k-1)
        exit
    endif
end
print(k)

で、$k$巻まで読めるかどうか。

$k$巻まで読めるかどうかの判定は、

  1. 最初に持っている本のうち、1巻から$k$巻までは2冊以上持っていれば1冊を残して売る … $O(k)$、全探索なら改良できる
  2. $k+1$巻よりあとは全部売る … $O(N)$、累積和つかえば改良できる
  3. 売った本で不足している本が買えればOK … 不足している冊数と売れる冊数が分かれば、$O(1)$。

これを$k = 1$から$N$までについて検討すればOK。
ただし愚直に検討すると1回の検討に$O(N)$かかるので全部で$O(N^2)$でTLE

計算時間短縮

kについて二分探索

公式解説の2番目の要領です。

コードはシンプルですが、結構高度なことをやっています。
C++の人が実装するなら、「$k$巻より前で重複のため売れる冊数」と「$k+1$巻以上の冊数」を累積和で管理することになるでしょうか。

$O(NlogN)$。

kについて工夫しながら全探索

愚直にやると上記のように$O(N^2)$ですが、$k$を1つずつ増やすので工夫の余地はありそうです。
色々なアプローチがありますが、以下の方法が圧倒的にシンプルなので採用しました↓

$k$巻以下で売らずに読む本をs冊とします。sの更新は$O(1)$でできます。
残りはn-s冊で、それらを全部売って$k$巻まで揃えばよいです。

ACコード

本番の際のACコードです。(コメントはあとで付加)

c.jl
function solver()
    n = toI()
    a = toVI()
    v = zeros(Int,300000) # v[i]は第i巻を何冊持っているかを表す
    for ai in a           # ai > 300000 は使用しないので無視してよい
        ai > 300000 && continue
        v[ai] += 1
    end

    s = 0                # 売らずに読むことのできることのできる冊数
    for i in 1:300000    # 第i-1巻まで読むことが可能なとき、第i巻を読むことができるか
        if v[i]  1      # 1冊以上あれば売らずに読むことができる
            s += 1
        end
        r = n - s        # i巻までのみ読むと仮定すると、s冊以外は全部売ることができる
        if s + r÷2 < i   # r冊売ってr÷2冊買って合計冊数がi冊に満たなければi巻までは読めないので打ち切り
            println(i-1)
            return
        end
    end
    println(300000)      # 打ち切られずにここまでくれば上界
end

toI(x=readline()) = parse(Int,x)
toVI() = [toI(x) for x  split(readline())]

solver()

Datastructures.jlcounterでもっと短いコードにできます。
内部でunordered_mapに相当するものを使用しているので速度は劣ります。

c_counter.jl
using DataStructures

function solver()
    n = toI()
    v = toVI() |> counter

    s = 0
    i = 0
    while true
        i += 1
        if v[i]  1
            s += 1
        end
        r = n - s
        if s + r÷2 < i
            println(i-1)
            return
        end
    end
end

toI(x=readline()) = parse(Int,x)
toVI() = [toI(x) for x  split(readline())]

solver()

JuliaはJITなのでコンパイル時間の分だけ不利ですが、1.4から1.8ではかなり改善しています。次の言語アップデートが待ち遠しいです。

1
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
1
0