E - A += v Editorial by harurun4635


「出現回数が \(c\) 回の要素を追加する操作」をまとめて「ステップ \(c\) 」と呼ぶことにします。

ステップ \(c\) は「\(A\) における出現回数が \(c\) 以下の値を昇順に追加する」ことになるはずです。このときに追加される値を順に並べた配列を \(B_c\) とします。

この \(B_c\) は高々 \(O(\sqrt N)\) 回しか変化しません。

なぜ?

集合としてみれば \(B_{c-1} \subseteq B_{c}\) です。 \(B_{c-1} \subsetneq B_{c}\) となるのは \(A\) において \(c\) 回出現する要素があるときです。

この \(c\) の種類数( \(=\) \(B_c\) の変化回数)を \(k\) とします。 \(k\) が最大となるのは、(一例としては)出現回数が \(k-1 + \alpha, k-2, \dots , 2, 1, 0, 0, \dots , 0, 0\) のようになっているときです。このときの出現回数の総和は \(\frac{k(k-1)}{2} + \alpha\) であり、\(\frac{k(k-1)}{2} \le N\) であることを考えれば \(k = O(\sqrt N)\) です。


よって、すべての \(B_c\) の列挙が( \(B_c\) がおなじ \(c\) を適切にまとめれば)空間・時間計算量ともに \(O(M\sqrt N)\) です。メモリに余裕があればこれを列挙しても構いませんが、今回の制約下ではおそらく MLE となるでしょう。

しかし、\(c\) の昇順にクエリを処理しながら \(B_c\) を適宜再構築すれば、空間計算量に余裕が生まれるため、高速な言語であれば AC 可能です。時間計算量は全体で \(O(M \sqrt N + Q \log Q)\) 空間計算量は \(O(N + M + Q)\) となります。

実装例 codon

from bisect import bisect_right
from heapq import heapify, heappop

n, m = [int(v) for v in input().split()]
a = [int(v) for v in input().split()]

c = [0] * (m + 1)
for x in a:
    c[x] += 1

mc = max(c)
ch = [0] * (mc + 1)
for x in range(1, m + 1):
    ch[c[x]] = 1

q = int(input())
que = [(int(input())-1, i) for i in range(q)]
heapify(que)
ans = [0] * q

while que and que[0][0] < n:
    x, i = heappop(que)
    ans[i] = a[x]

l = n
b = []
for nc in range(mc + 1):
    if ch[nc]:
        b = [x for x in range(1, m + 1) if c[x] <= nc]
    l += len(b)  
    while que and que[0][0] < l:
        x, i = heappop(que)
        ans[i] = b[x-l+len(b)]
else:
    while que:
        x, i = heappop(que)
        ans[i] = (x-l) % m + 1

print("\n".join(map(str, ans)))

p.s. 空間計算量に余裕があれば、この解法はオンラインクエリに対応できる解法です。しかし、クエリ先読みをするのであれば、想定解の実装にほとんど合流する(対数時間の要素の追加ではなく、単に愚直な再構築でよいというのみ)ということになるでしょう。

posted:
last update: