Nachia's blog

By Nachia, history, 13 months ago, In English

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.net/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.

  • Vote: I like it
  • +134
  • Vote: I do not like it

»
13 months ago, # |
  Vote: I like it 0 Vote: I do not like it

Auto comment: topic has been updated by Nachia (previous revision, new revision, compare).

»
13 months ago, # |
  Vote: I like it +47 Vote: I do not like it

Beautiful algorithm

»
13 months ago, # |
  Vote: I like it 0 Vote: I do not like it

Nice. It reminds me of the suffix array algorithm

»
13 months ago, # |
  Vote: I like it +25 Vote: I do not like it

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.

  • »
    »
    13 months ago, # ^ |
      Vote: I like it +1 Vote: I do not like it

    Thank you! I didn't noticed that aspect (and approach).

»
13 months ago, # |
  Vote: I like it +13 Vote: I do not like it

I also created a problem in such settings: Baby Seokhwan. It was used in a CSAcademy Round.

»
13 months ago, # |
  Vote: I like it 0 Vote: I do not like it

Auto comment: topic has been updated by Nachia (previous revision, new revision, compare).

»
13 months ago, # |
  Vote: I like it +23 Vote: I do not like it

We can sometimes use this deterministic algorithm instead of randomized hash.

How to solve this problem using randomized hash?

  • »
    »
    13 months ago, # ^ |
      Vote: I like it +8 Vote: I do not like it

    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