Wavelet Matrix
値を 2 進数で上の桁から見ていき、各桁で、その桁が $0$ のものを前に、 $1$ のものを後ろに(それぞれ元の順番のまま)並べ替えた列を、桁の数だけ作ります。各段で「先頭 $i$ 個のうち、その桁が $1$ のものの個数」を持っておくと、区間が次の段のどの区間に移るかがすぐにわかります。
列の値は $0$ 以上の整数とし、 $A$ を値の最大値とします。区間はすべて半開区間 $[l, r)$ です。
WaveletMatrix(a): 列 $a$ から前計算。 $O(N \log A)$kth_smallest(l, r, k),kth_largest(l, r, k):a[l:r]の小さい方・大きい方から $k$ 番目(0-indexed)の値range_freq(l, r, lower, upper):a[l:r]のうち $\mathrm{lower}$ 以上 $\mathrm{upper}$ 未満の値の個数range_sum(l, r, lower, upper):a[l:r]のうち $\mathrm{lower}$ 以上 $\mathrm{upper}$ 未満の値の和prev_value(l, r, upper):a[l:r]のうち $\mathrm{upper}$ 未満の最大値(なければNone)next_value(l, r, lower):a[l:r]のうち $\mathrm{lower}$ 以上の最小値(なければNone)sum_smallest(l, r, k):a[l:r]の小さい方から $k$ 個の和( $0 \le k \le r - l$)- 計算量: 前計算以外はどれも $O(\log A)$
負の値があるときは、すべての値に同じ数を足して $0$ 以上にしてから使います。値の種類が少ないときは 座標圧縮 すると段が減って速くなります(このとき和は圧縮したあとの値の和になります)。
コード
class WaveletMatrix:
# 0 以上の整数の列 a に対する区間のクエリ(値の更新はできない)
def __init__(self, a):
n = len(a)
self.log = log = max(max(a, default=0).bit_length(), 1)
self.pre = [0] * (n + 1) # a の累積和
for i, x in enumerate(a):
self.pre[i + 1] = self.pre[i] + x
self.ones = [] # ones[j][i] = 段 j の先頭 i 個のうち、見ているビットが 1 のものの個数
self.zeros = [] # zeros[j] = 段 j で見ているビットが 0 のものの個数
self.sums = [] # sums[j][i] = 段 j で並べ替えたあとの先頭 i 個の和
cur = list(a)
for d in range(log - 1, -1, -1):
o = [0] * (n + 1)
zero = []
one = []
for i, x in enumerate(cur):
if x >> d & 1:
o[i + 1] = o[i] + 1
one.append(x)
else:
o[i + 1] = o[i]
zero.append(x)
cur = zero + one
s = [0] * (n + 1)
for i, x in enumerate(cur):
s[i + 1] = s[i] + x
self.ones.append(o)
self.zeros.append(len(zero))
self.sums.append(s)
def kth_smallest(self, l, r, k):
# a[l:r] の小さい方から k 番目(0-indexed)
res = 0
for j in range(self.log):
o = self.ones[j]
zl, zr = l - o[l], r - o[r]
if k < zr - zl:
l, r = zl, zr
else:
k -= zr - zl
res |= 1 << (self.log - 1 - j)
l, r = self.zeros[j] + o[l], self.zeros[j] + o[r]
return res
def kth_largest(self, l, r, k):
# a[l:r] の大きい方から k 番目(0-indexed)
return self.kth_smallest(l, r, r - l - 1 - k)
def sum_smallest(self, l, r, k):
# a[l:r] の小さい方から k 個の和
if k == 0:
return 0
k -= 1
res = 0
val = 0
for j in range(self.log):
o = self.ones[j]
zl, zr = l - o[l], r - o[r]
if k < zr - zl:
l, r = zl, zr
else:
k -= zr - zl
res += self.sums[j][zr] - self.sums[j][zl]
val |= 1 << (self.log - 1 - j)
l, r = self.zeros[j] + o[l], self.zeros[j] + o[r]
return res + (k + 1) * val
def _less(self, l, r, upper):
# a[l:r] のうち upper 未満の値の (個数, 和)
if upper <= 0:
return 0, 0
if upper >> self.log:
return r - l, self.pre[r] - self.pre[l]
cnt = 0
s = 0
for j in range(self.log):
o = self.ones[j]
zl, zr = l - o[l], r - o[r]
if upper >> (self.log - 1 - j) & 1:
cnt += zr - zl
s += self.sums[j][zr] - self.sums[j][zl]
l, r = self.zeros[j] + o[l], self.zeros[j] + o[r]
else:
l, r = zl, zr
return cnt, s
def range_freq(self, l, r, lower, upper):
# a[l:r] のうち lower 以上 upper 未満の値の個数
if lower >= upper:
return 0
return self._less(l, r, upper)[0] - self._less(l, r, lower)[0]
def range_sum(self, l, r, lower, upper):
# a[l:r] のうち lower 以上 upper 未満の値の和
if lower >= upper:
return 0
return self._less(l, r, upper)[1] - self._less(l, r, lower)[1]
def prev_value(self, l, r, upper):
# a[l:r] のうち upper 未満の最大値(なければ None)
c = self._less(l, r, upper)[0]
return None if c == 0 else self.kth_smallest(l, r, c - 1)
def next_value(self, l, r, lower):
# a[l:r] のうち lower 以上の最小値(なければ None)
c = self._less(l, r, lower)[0]
return None if c == r - l else self.kth_smallest(l, r, c)