Problem Statement in English
You are given an array of integers nums and an integer k.
You can choose a non empty subarray that you didn’t choose previously. You must then pick the number from that array with the highest “prime score”. If there are multiple numbers with the same prime score, you must choose the one with the smallest index.
The prime score of a number is the number of distinct prime factors of that number.
You must now multiply your answer by the number you just chose.
You can repeat this process atmost k times.
Since the answer may be very large, return it modulo 10^9 + 7.
Approach
Whew. There’s a lot going on here. Let’s break it down.
Calculating the prime score of each number: We can use a modified Sieve of Eratosthenes to find the number of distinct prime factors for each number in
nums. This will allow us to determine the prime score of each number efficiently.The sieve works by iterating through each number, and for each number that is prime, we mark its multiples. That’s the regular sieve. The modification we make, is that for each multiple, we increment the count of distinct prime factors. This way, we can find the prime score for all numbers up to the maximum number in
nums.Finding the left and right ranges: We need to find the nearest previous and next indices with strictly greater prime scores. This will help us calculate how many times each number can dominate others. This is done using a montonic stack approach.
In order to find the left range, we iterate through the array (from left to right) and maintain a stack of indices. For each index, we pop from the stack until we find an index with a greater prime score. The left range for the index we’re processing is the first index whose prime score is greater than this one’s.
In order to find the right range, we do the same but iterate from right to left. The right range for an index is the first index we encoutner whilst popping, whose prime score is greater than or equal to this one’s.
Calculating the dominance count: For each number, we can calculate how many times it can dominate others by using the left and right ranges. The dominance count for an index
iis given by the formula(right_range[i] - i) * (i - left_range[i]). This gives us the number of indices that this number can dominate. Personally, I have no idea who came up with this formula, but it works. Some things are just meant to be.Finding out the top k subarrays to use: We can use a max heap to keep track of the numbers and their dominance counts. This allows us to efficiently get the maximum product by popping the largest number from the heap and multiplying it with the result.
And with that, we’re finally done.
Solution in Python
from heapq import heapify, heappop
from collections import defaultdict
from math import ceil, sqrt
from typing import List
class Solution:
def maximumScore(self, nums: List[int], k: int) -> int:
MOD = 1_000_000_007
# Edge case: empty nums
if not nums:
return 0
# Find distinct prime factors count using modified Sieve of Eratosthenes
largest = max(nums)
factors = [0] * (largest + 1)
for i in range(2, sqrt(largest) + 1):
if factors[i] == 0: # Prime number
for j in range(i, largest + 1, i):
factors[j] += 1
# Compute left and right ranges for dominance calculation
LEN = len(nums)
left_range, right_range = [-1] * LEN, [LEN] * LEN
stack = []
# Calculate left_range: nearest previous index with strictly greater factors
for i in range(LEN):
while stack and factors[nums[stack[-1]]] < factors[nums[i]]:
stack.pop()
left_range[i] = stack[-1] if stack else -1
stack.append(i)
stack = []
# Calculate right_range: nearest next index with greater or equal factors
for i in range(LEN - 1, -1, -1):
while stack and factors[nums[stack[-1]]] <= factors[nums[i]]:
stack.pop()
right_range[i] = stack[-1] if stack else LEN
stack.append(i)
# Compute dominance count
dominatesFor = [(right_range[i] - i) * (i - left_range[i]) for i in range(LEN)]
# Use max heap to get the maximum product
maxheap = [(-nums[i], dominatesFor[i]) for i in range(LEN)]
heapify(maxheap)
res = 1
while k and maxheap:
num, times = heappop(maxheap)
take = min(k, times)
k -= take
res = (res * pow(-num, take, MOD)) % MOD
return res
Complexity
Time: $O(klog(n))$
This is because we are using a max heap to keep track of the numbers and their dominance counts. Each pop operation takes $O(log(n))$ time, and we do this at mostktimes.Space: $O(n)$
Since we are storing the prime factors and the left and right ranges in separate arrays, the space complexity is linear with respect to the size of the input array.
And we are done.