PROBLEM LINK:
Contest - Division 3
Contest - Division 2
Contest - Division 1
DIFFICULTY:
EASY-MEDIUM
PREREQUISITES:
Segment Tree, Greedy, Square Root Decomposition
PROBLEM:
Given an array of N positive integers. You have to answer Q queries, each requiring you to determine the maximum possible GCD over all subsequences (of length >1) of a specified range of the array.
EXPLANATION:
Hint 1
Observe that it is sufficient to select only 2 elements in the specified range to achieve the maximum possible gcd.
Why?
It is known that:
Therefore, since the gcd never increases on adding another element, it makes sense to select the least possible number of elements.
Hint 2
Armed with this observation, write a naive bruteforce to solve the problem in O(N^2) per query.
Solution
For each element in the range, compute the gcd with every other element in the range. The answer is then the maximum over these computed values.
This is too slow to pass the subtasks apart from the 1^{st} one.
Hint 3
Let d(x) represent the number of divisors of positive integer x. Then, for a fixed a, there are exactly d(a) possible values of \gcd(a,b) over all valid b.
Thus, instead of iterating over every pair of elements, can you iterate on the divisors of every element (in the range) to calculate the maximum possible gcd?
Solution
Let D_x represent the set of divisors of integer x.
Let S be the multiset of \{D_{A_l}, D_{A_{l+1}},\dots,D_{A_r}\}. Then, it is easily verifiable that the maximum possible gcd is the largest number in S that occurs more than once.
Precomputing divisors for all A_i can be done in \approx O(N\sqrt{\max A_i}). Then, the above method can determine the solution in O(N\cdot d) per query, where d\le 100 represents the maximum number of divisors (for the given constraints).
This is an improvement to our naive bruteforce, but will only pass the first two subtasks.
Hint 3
Offline (aka, no updates) range queries is a significant pointer towards sorting the queries first and then solving.
Solution for subtask 3; Unrelated to complete solution
Standard application of Mo’s Algorithm. The time complexity is O((N+Q)\cdot d\cdot\sqrt N), which works for the first 3 subtasks.
Solution
Start by sorting all queries in increasing order of their right (R value) bound.
We iterate over array A from 1 to N. Let i represent the current index we are processing. Also, let v_x represent the maximum gcd over all pairs of elements in the subarray A[x..i]. Then, answer to any query of the form [l, i] equals v_l.
Initially, v_x is 0 for all x.
Here’s how we can efficiently update array v in each iteration -
Traverse over all divisors of A_i - call the current divisor d. Consider the largest p<i such that d\mid A_p. Then, update v_x = \max(v_x,d) for all x\le p.
It is easy to see (by recursion) that at the end of the above operation, the updated array matches the aforementioned definition of v.
How do we implement this?
The value of p can be efficiently determined in O(1) by maintaining a sliding window during our iteration. Range updates (and point queries) to v can be done in O(\log N) using a segment tree with lazy propagation.
The total time complexity is therefore:
This however, TLE’s on exactly one test in the last subtask!
To remedy this, observe that the number of updates is a magnitude more than the number of queries. Thus, we could switch to a data structure with faster updates and slightly slower queries - Square Root Decomposition.
For this, we must appropriately convert our operations to be point update (giving O(1) per update) and range query (with O(\sqrt N) per query), which is left to the reader as a simple exercise.
TIME COMPLEXITY:
Precomputing divisors of each integer takes O(N\sqrt{\max A_i}).
Sorting the queries takes O(Q\log Q). Building array v for each index i takes O(d), where d is the number of divisors of A_i. Determining the answer to each query takes O(\sqrt N) with range queries. The total time complexity is therefore
per test case.
SOLUTIONS:
Editorialist’s solution can be found TBD