#P2166. Heapsort

Heapsort

中文题目:

一个著名的算法叫做堆排序,它是一种确定性排序算法,时间复杂度为O(nlogn)O(n \log n),额外空间复杂度为O(1)O(1)。下面让我们来描述对一个由不同整数组成的数组进行升序排序的过程。该算法由两个阶段组成。在第一阶段,称为建堆(heapification),待排序的整数数组会被转换为一个堆。如果对于所有满足1in1 \leq i \leq n。的ii,以下堆的条件都成立,那么整数数组a[1n]a[1…n]就被称为一个堆:如果2in2i≤n,那么a[i]>a[2i]a[i]>a[2i];如果2i+1n2i+1≤n,那么a[i]>a[2i+1]a[i]>a[2i+1]。我们可以将一个数组解释为一棵二叉树,把元素a[i]a[i]的子节点看作是a[2i]a[2i]a[2i+1]a[2i+1]。在这种情况下,a[i]a[i]的父节点是a[ia[i div 2]2],其中ii div 22=[i/2][i/2](向下取整)。从树的角度来看,成为一个堆的性质意味着对于每个节点,它的值都大于其孩子节点的值。在第二阶段,堆会被转换为一个已排序的数组。由于堆的条件,建堆后的数组中最大的元素是a[1]a[1]。让我们将它a[n]a[n]交换,现在数组中最大的元素就在已排序数组中的正确位置了。这被称为提取最大值(extract-max)。现在让我们考虑数a[1n1]a[1…n−1]这一部分。它可能不再是一个堆,因为对于i=1i=1时堆的条件可能不成立。如果是这样的情况(也就是说,要么a[2]a[2]大于a[1]a[1],要么a[3]a[3]大于a[1]a[1],或者两者都大于a[1]a[1]),让我们将a[1]a[1]的最大子节点与它交换,恢复i=1i=1时的堆条件。现在,可能在现在包含a[1]a[1]之前值的那个位置上堆条件又不成立了。对该位置应用相同的过程,将它与它的最大子节点交换。如此进行下去,我们将整个数组a[1n1]a[1…n−1]转换为一个堆。这个过程被称为向下筛(sifting down)。在通过向下筛选将a[1n1]a[1…n−1]这部分转换为堆之后,我们再次应用提取最大值操作,将数组中第二大的元素放到a[n1]a[n−1]的位置,以此类推例如,让我们看看堆a=(5,4,2,1,3)a=(5,4,2,1,3)是如何被转换为一个已排序数组的。让我们进行第一次提取最大值操作。之后数组变为(3,4,2,1,5)(3,4,2,1,5)。对于a[1]=3a[1]=3,堆条件不成立,因为它的子节点a[2]=4a[2]=4大于它。让我们对它进行向下筛选,交换a[1]a[1]a[2]a[2]。现在数组是(4,3,2,1,5)(4,3,2,1,5)。对于所有元素,堆条件都满足了,所以向下筛选结束。让我们再次进行提取最大值操作。现在数组变为(1,3,2,4,5)(1,3,2,4,5)。对于a[1]a[1],堆条件又不成立了;将它与它的最大子节点交换,我们得到数组(3,1,2,4,5)(3,1,2,4,5) ,这是一个正确的堆。所以我们进行提取最大值操作,得到 (2,1,3,4,5)(2,1,3,4,5) 。这次对于所有元素,堆条件都满足了,所以我们进行提取最大值操作,得到 (1,2,3,4,5)(1,2,3,4,5) 。数组的前导部分是一个堆,最后一次提取最大值操作最终得到 (1,2,3,4,5)(1,2,3,4,5) 。 已知建堆可以在O(n)O(n)时间内完成。因此,在堆排序算法中最耗时的操作是向下筛选,它的时间复杂度为 O(nlogn)O(n \log n)。在这个问题中,你需要找到一个由从 1到n的不同数字组成的建堆后的数组,使得当将它转换为一个已排序数组时,在所有向下筛选操作中交换的总次数尽可能达到最大。在上面的例子中,交换次数是1+1+0+0+0=2,这不是最大值。对于n=5,数组 (5,4,3,2,1) 给出了最大交换次数 4次。

输入

输入包含 nn1n500001≤n≤50000)。

输出:

输出一个由从1到n的n个不同整数组成的数组,使得它是一个堆,并且当将它转换为一个已排序数组时,在向下筛选操作中交换的总次数尽可能达到最大。数字之间用空格分隔。