Hard
Minimum Time For K Virus Variants to Spread — Python
Full explanation · Time O(nlogn * logr) · Space O(n)
# Time: O(nlogn * logr), r is the sum of range x size and range y size
# Space: O(n)
# Range Maximum Query
class SegmentTree(object): # 0-based index
def __init__(self, N,
build_fn=lambda x, y: [y]*(2*x),
query_fn=lambda x, y: y if x is None else max(x, y),
update_fn=lambda x, y: y if x is None else x+y,
default_val=0):
self.N = N
self.H = (N-1).bit_length()
self.query_fn = query_fn
self.update_fn = update_fn
self.default_val = default_val
self.tree = build_fn(N, default_val)
self.lazy = [None]*N
def __apply(self, x, val):
self.tree[x] = self.update_fn(self.tree[x], val)
if x < self.N:
self.lazy[x] = self.update_fn(self.lazy[x], val)
def update(self, L, R, h): # Time: O(logN), Space: O(N)
def pull(x):
while x > 1:
x //= 2
self.tree[x] = self.query_fn(self.tree[x*2], self.tree[x*2+1])
if self.lazy[x] is not None:
self.tree[x] = self.update_fn(self.tree[x], self.lazy[x])
L += self.N
R += self.N
L0, R0 = L, R
while L <= R:
if L & 1: # is right child
self.__apply(L, h)
L += 1
if R & 1 == 0: # is left child
self.__apply(R, h)
R -= 1
L //= 2
R //= 2
pull(L0)
pull(R0)
def query(self, L, R): # Time: O(logN), Space: O(N)
def push(x):
n = 2**self.H
while n != 1:
y = x // n
if self.lazy[y] is not None:
self.__apply(y*2, self.lazy[y])
self.__apply(y*2 + 1, self.lazy[y])
self.lazy[y] = None
n //= 2
result = None
if L > R:
return result
L += self.N
R += self.N
push(L)
push(R)
while L <= R:
if L & 1: # is right child
result = self.query_fn(result, self.tree[L])
L += 1
if R & 1 == 0: # is left child
result = self.query_fn(result, self.tree[R])
R -= 1
L //= 2
R //= 2
return result
def __str__(self):
showList = []
for i in xrange(self.N):
showList.append(self.query(i, i))
return ",".join(map(str, showList))
# competitive programming solution
class Solution(object):
def minDayskVariants(self, points, k):
"""
:type points: List[List[int]]
:type k: int
:rtype: int
"""
def add_rec(rec, intervals):
x0, y0, x1, y1 = rec
# add [y0, y1] by 1 in [x0, x1+1)
intervals.append([[x0, +1], [y0, y1]])
intervals.append([[x1+1, -1], [y0, y1]])
def check(points, k, l): # Time: O(nlogn), Space: O(n)
intervals = []
y_set = set()
for x, y in points:
add_rec([x-l, y-l, x+l, y+l], intervals)
y_set.add(y-l)
y_set.add(y+l)
intervals.sort()
y_to_idx = {y:i for i, y in enumerate(sorted(y_set))} # coordinate compression
st = SegmentTree(len(y_to_idx))
for [_, v], [y0, y1] in intervals: # line sweep
st.update(y_to_idx[y0], y_to_idx[y1], v)
if st.query(0, len(y_to_idx)-1) >= k:
return True
return False
points = [[x+y, x-y] for x, y in points] # rotate
min_x = min(points)[0]
max_x = max(points)[0]
min_y = min(points, key=lambda x: x[1])[1]
max_y = max(points, key=lambda x: x[1])[1]
left, right = 0, ((max_x-min_x)+(max_y-min_y)+1)//2
while left <= right:
mid = left + (right-left)//2
if check(points, k, mid):
right = mid-1
else:
left = mid+1
return left
# Time: O(n^2 * logr), r is the sum of range x size and range y size
# Space: O(n)
import collections
# interview solution
class Solution2(object):
def minDayskVariants(self, points, k):
"""
:type points: List[List[int]]
:type k: int
:rtype: int
"""
def add_rec(rec, intervals):
x0, y0, x1, y1 = rec
# add [y0, y1+1) by 1 in [x0, x1+1)
intervals[x0][y0] += 1
intervals[x0][y1+1] -= 1
intervals[x1+1][y0] -= 1
intervals[x1+1][y1+1] += 1
def check(points, k, l): # Time: O(n^2), Space: O(n)
intervals = collections.defaultdict(lambda:collections.defaultdict(int))
y_set = set()
for x, y in points:
add_rec([x-l, y-l, x+l, y+l], intervals)
y_set.add(y-l)
y_set.add(y+l+1)
sorted_y = sorted(y_set)
sorted_x = sorted(intervals.iterkeys())
count = collections.Counter()
for x in sorted_x: # line sweep
for y, c in intervals[x].iteritems():
count[y] += c
cnt = 0
for y in sorted_y:
cnt += count[y]
if cnt >= k:
return True
return False
points = [[x+y, x-y] for x, y in points] # rotate
min_x = min(points)[0]
max_x = max(points)[0]
min_y = min(points, key=lambda x: x[1])[1]
max_y = max(points, key=lambda x: x[1])[1]
left, right = 0, ((max_x-min_x)+(max_y-min_y)+1)//2
while left <= right:
mid = left + (right-left)//2
if check(points, k, mid):
right = mid-1
else:
left = mid+1
return left