I got AC on Codeforces Round 905 (Div. 1) C. Minimum Array with my prewritten code sorting all arrays obtained (in lexicographic order) in $$$\mathcal{O}(n\log n + q\log q)$$$ time.
https://codeforces.me/contest/1887/submission/229244614
How is the processing time achieved? I made a tutorial (in Japanese) before. (https://www.mathenachia.blog/sortseqs/ and https://nachiavivias.github.io/cp-library/cpp/array/point-update-lex-sort.html) This time I make a brief explanation in English.
Problem
First you are given an array $$$(A _ 0[0],A _ 0[1],\ldots ,A _ 0[N-1])$$$ of length $$$N$$$ . You will construct other $$$Q$$$ arrays $$$A _ 1, A _ 2, \ldots , A _ Q$$$ as follows :
- For $$$k=1,2,\ldots ,Q$$$ (in order) , you are given an integer $$$p _ k$$$ $$$( 0 \leq p _ k \leq N - 1 )$$$ and a value $$$x _ k$$$ . Overwrite $$$A _ k$$$ with the copy of $$$A _ {k-1}$$$ and change the value of $$$A _ {k}[p _ k]$$$ to $$$x _ k$$$ .
Find an array $$$(F _ 0,F _ 1,F _ 2,\ldots ,F _ Q)$$$ of nonnegative integers such that :
- $$$F _ i \lt F _ j \iff A _ i\lt A _ j$$$ (in lexicographic order) holds for $$$0 \leq i \leq Q$$$ , $$$0 \leq j \leq Q$$$ .
- Maximum value of $$$(F _ 0,F _ 1,F _ 2,\ldots ,F _ Q)$$$ is minimized.
In other words, sort all $$$Q+1$$$ arrays in lexicographic order.
Algorithm
Above I wrote like $$$A _ a[b]$$$ , so I call $$$a$$$ as time index and $$$b$$$ as array index .
Divide and Conquer array index . After we could sort every half, we can get full answer in linear time with radix sort (sort by second digit, then stable sort by first digit) .
When we divide array index, changing points are also divided in two groups. So we can compress time index . We can bound the sum of number of time index as $$$\mathcal{O}(N+Q)$$$ in any layer of dividing. Of cource the number of the layers is $$$\mathcal{O}(\log N)$$$ . Therefore the entire process takes $$$\mathcal{O}((N+Q) \log (N+Q))$$$ time ( the term $$$Q\log Q$$$ is for sorting given values ).
Main Usage
We can sometimes use this deterministic algorithm instead of randomized hash.
Supplement Based on Comments
Thank you for your helpful comments.
- You can search Merkle tree for a broader perspective on this topic.
- Baekjoon Online Judge already has a judge for a similar problem at least as hard as this example.
Auto comment: topic has been updated by Nachia (previous revision, new revision, compare).
Beautiful algorithm
Nice. It reminds me of the suffix array algorithm
You could also use (persistent) Merkel trees to solve this problem. And you can also avoid hashes: just set unique identifiers to nodes and keep a global hashmap that assigns a pair $$$(label_l, label_r)$$$ to the label of the root node.
This approach, however, has a worse constant than what you’re describing, but at the same time feels like it could be applied in a little bit more general scenarios.
Thank you! I didn't noticed that aspect (and approach).
I also created a problem in such settings: Baby Seokhwan. It was used in a CSAcademy Round.
Thank you for information!
Auto comment: topic has been updated by Nachia (previous revision, new revision, compare).
How to solve this problem using randomized hash?
Let $$$(w_0, \ldots, w_{n-1})$$$ be a randomly generated sequence. By hash of a subsegment $$$[l, r]$$$ of array $$$a$$$ we will denote the value of $$$w_la_l + \ldots + w_ra_r$$$. If we build a persistent segment tree that stores the hashes, we can compare two versions of the array by finding the length of the minimal prefix with different hashes in these versions, then somehow compare the corresponding elements. However, I couldn't meet the memory limit with this approach