D - 中央値チェック / Median Check 解説 by admin
gpt-5.3-codexOverview
This problem requires counting the number of times the median matches the deleted value at the moment just before deletion, while adding and removing values from a multiset through queries.
To efficiently compute the median at each point in time, we use coordinate compression + Fenwick Tree (BIT).
Analysis
First, for each deletion query - x, we need to determine the “current median.”
The median is the \(\lceil n/2 \rceil\)-th element in ascending order, where \(n\) is the number of elements. In code, this is the \(k = (n+1)//2\)-th value.
Why a Naive Solution Is Too Slow
For example, if we were to: - Re-sort the multiset each time, or - Insert/delete from a list and look up the median position,
each query would take \(O(n)\) or more, which is too slow for \(Q \le 2\times10^5\) (TLE).
Key Insight
What we need is “the relative ordering of values” and “how many of each value exist.”
Therefore:
- Collect all \(X_i\) in advance, sort and deduplicate them (coordinate compression).
- Use a Fenwick Tree to manage counts indexed by compressed coordinates.
- Use prefix sums on the Fenwick Tree to find the “\(k\)-th element” via a binary-search-like method.
This allows additions, deletions, and median queries all in \(O(\log Q)\).
Algorithm
- Read all queries in advance and collect the appearing values into
vals. - Sort
valsand remove duplicates to createvals_sorted(coordinate compression).comp[x] = compressed index (1-indexed)
- Prepare a Fenwick Tree
fw, wherefw[i]represents “the count of compressed value i.” - Maintain
sizeas the current number of elements. - Process each query in order:
+ x:fw.add(comp[x], 1),size += 1- x:- Compute \(k = (size+1)//2\) (the rank of the current median).
- Obtain the compressed position of the \(k\)-th element with
mid_idx = fw.kth(k). - If the actual value
vals_sorted[mid_idx-1]equalsx, increment the answer by 1. - Then perform
fw.add(comp[x], -1),size -= 1.
fw.kth(k) is a function that returns “the smallest index where the prefix sum first reaches \(k\) or more,” which directly corresponds to the \(k\)-th element.
Complexity
- Time complexity: \(O(Q \log Q)\)
(Sorting for coordinate compression \(O(Q\log Q)\) + Fenwick Tree operations per query \(O(\log Q)\)) - Space complexity: \(O(Q)\)
(Query storage, compression array, BIT)
Implementation Notes
The median must be determined on the set before deletion (check first, then delete).
The problem’s median definition is “for an even number of elements, the smaller of the two middle values,” so the rank is always \(k=(size+1)//2\).
Implementing the Fenwick Tree as 1-indexed makes it easier to handle.
The
kthimplementation uses the internal array of the BIT to locate the position in a binary-search-like manner. This enables rank queries in \(O(\log N)\).Source Code
import sys
class Fenwick:
def __init__(self, n):
self.n = n
self.bit = [0] * (n + 1)
def add(self, i, x):
while i <= self.n:
self.bit[i] += x
i += i & -i
def sum(self, i):
s = 0
while i > 0:
s += self.bit[i]
i -= i & -i
return s
def kth(self, k):
idx = 0
bit_mask = 1 << (self.n.bit_length() - 1)
while bit_mask:
nxt = idx + bit_mask
if nxt <= self.n and self.bit[nxt] < k:
k -= self.bit[nxt]
idx = nxt
bit_mask >>= 1
return idx + 1
def main():
input = sys.stdin.readline
Q = int(input())
queries = []
vals = []
for _ in range(Q):
op, x = input().split()
x = int(x)
queries.append((op, x))
vals.append(x)
vals_sorted = sorted(set(vals))
comp = {v: i + 1 for i, v in enumerate(vals_sorted)}
fw = Fenwick(len(vals_sorted))
size = 0
ans = 0
for op, x in queries:
idx = comp[x]
if op == '+':
fw.add(idx, 1)
size += 1
else:
k = (size + 1) // 2
mid_idx = fw.kth(k)
if vals_sorted[mid_idx - 1] == x:
ans += 1
fw.add(idx, -1)
size -= 1
print(ans)
if __name__ == "__main__":
main()
This editorial was generated by gpt-5.3-codex.