Next Permutation on Subsegment

Правка en10, от Guslix, 2021-08-20 09:41:35

Is it yet another range query for $$$O(log N)$$$? RSQ, RMQ, average, reversal on subsegment and even... next_permutation? Yes, of course! This problem an be solved using a simple and very old Narayana's algorithm. This algorithm invented by Indian mathematician Pandit Narayana more than 600 years ago.

Given array $$$a$$$. How to go to its next permutation?

  1. Find the longest non-increasing suffix of the array (LNIS). If whole array is non-increasing, permutations are exhausted.

  2. Let LNIS be a suffix $$$k$$$, where $$$k$$$ is position where it begins. Swap previous element, $$$a_{k-1}$$$ and the next largest number (NLN) that exists in suffix $$$k$$$.

  3. Reverse suffix $$$k$$$.

Learn more about Narayana's algorithm

This operations can be performed on a treap! Reversal is known, and swapping is just a combination of split and merge operations. Also we need to find LNIS and NLN. LNIS is sorted by definition, so we can apply binary search in order to find NLN of $$$a_{k-1}$$$.

But to find LNIS is more difficult. Let in each subtree maintain if the elements in this tree are non-increasing. Then recursion on the treap selects at most one subtree each step and find LNIS for $$$O(log N)$$$.

How to maintain this flag? Elements of a tree are non-increasing if and only if both subtrees are with non-increasing elements and last element of left subtree $$$\geq$$$ value of tree's root $$$\geq$$$ first element of right subtree. Empty subtrees don't affect the non-increasing order. Since we need to reverse segments, we should to maintain also a flag of non-decreasing order. These two flags are swapping while reversing.

The Code

Struct node
Recalc of size and other node parameters
Lazy propagation of reverse
Split
Merge
Build a treap from array
Find k, the position of start LNIS
Find the position of NLN
void next_p(int l, int r, treap t=root){
    treap lt,rt, swap1,swap2, t2;
    split(t,t,rt,r+1);
    int tail = tail(t);
    if(tail==l){
        t->reversed^=1; //just reverse it
        merge(t,t,rt); return;
    }
    l=tail; //elements to the left aren't changed
    split(t,lt,t,l-1); split(t,swap1,t,l); //separate p_(n-k)
    t->reversed^=1; //it's easier to reverse tail k before finding p_next
    int p_next = upper_b(l,r,t); //find p_next by binary search
    split(t,t2,t,p_next); split(t,swap2,t,p_next+1); //separate p_next
    merge(t,swap1,t); merge(t,t2,t); merge(t,swap2,t); //unite tail k+1
    merge(t,lt,t); merge(t,t,rt); //unite all
}
Теги next permutation, narayana, treap, data structures

История

 
 
 
 
Правки
 
 
  Rev. Язык Кто Когда Δ Комментарий
en18 Английский Guslix 2021-08-21 14:15:26 45
en17 Английский Guslix 2021-08-20 20:00:01 51
en16 Английский Guslix 2021-08-20 19:58:14 53
en15 Английский Guslix 2021-08-20 19:51:12 1 Tiny change: '$O(\log n).$\n\n\n\n<' -> '$O(\log n)$\n\n\n\n<'
en14 Английский Guslix 2021-08-20 19:31:34 51 (published)
en13 Английский Guslix 2021-08-20 18:35:11 422
en12 Английский Guslix 2021-08-20 18:18:48 1405
en11 Английский Guslix 2021-08-20 15:34:52 1142
en10 Английский Guslix 2021-08-20 09:41:35 450
en9 Английский Guslix 2021-08-19 23:59:09 698
en8 Английский Guslix 2021-08-19 20:59:20 129
en7 Английский Guslix 2021-08-19 20:20:06 5
en6 Английский Guslix 2021-08-19 20:19:06 15
en5 Английский Guslix 2021-08-19 20:15:03 548
en4 Английский Guslix 2021-08-19 19:55:57 3193
en3 Английский Guslix 2021-08-19 19:40:49 670
en2 Английский Guslix 2021-08-19 19:01:53 402
en1 Английский Guslix 2021-08-19 10:54:29 1255 Initial revision (saved to drafts)