【2022牛客多校-2】G Link with Monotonic Subsequence

86 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第25天,点击查看活动详情

【2022牛客多校-2】G Link with Monotonic Subsequence

题目链接

ac.nowcoder.com/acm/contest…

题目

First, let's review some definitions. Feel free to skip this part if you are familiar with them.

A sequence aa is an increasing (decreasing) subsequence of a sequence bb if aa can be obtained from bb by deletion of several (possibly, zero or all) elements and all elements are in increasing (decreasing) order from the beginning to the end.

A permutation is an array consisting of nn distinct integers from 11 to nn in arbitrary order. For example, [2,3,1,5,4][2,3,1,5,4] is a permutation, but [1,2,2][1,2,2] is not a permutation (22 appears twice in the array) and [1,3,4][1,3,4] is also not a permutation (n=3n=3 but there is 44 in the array).

The problem starts from here.

Link has an array. He is currently learning the longest increasing subsequence algorithm. So he comes up with the following question.

Let the value of a permutation pp be max(lis(p),lds(p))\max⁡(lis(p),lds(p)), where lis(p)lis(p) is the length of the longest increasing subsequence of ppp and lds(p){\rm lds}(p)lds(p) is the length of the longest decreasing subsequence of pp. For all permutations of length nn, which one has the minimum value?

题目大意

构造一个排列,使其 max(lis(p),lds(p))max(lis(p), lds(p)) 最小。

思路

排列权值的最小值为 n⌈\sqrt{n}⌉ ,即对于一个长度为n的全排列, max(lis(p),lds(p))max(lis(p), lds(p)) 的最小值是 n⌈\sqrt{n}⌉ 的上取整。证明如下:

记排列中的第 ii 个元素为 aia_i ,对于排列中的每个元素,我们记一个二元组 (lisi,ldsi)(lis_i ,lds_i) ,其中 lisilis_i 表示以第 i 个数结尾的最长上升子序列长度,ldsilds_i 表示以第 i 个数结尾的最长下降子序列长度。

对于每个排列生成的所有二元组中的任意两个二元组,下标分别记为 i,ji,ji<ji<j

  1. ai>aja_i>a_j,则 ldsjldsi+1lds_j\ge lds_i+1
  2. ai<aja_i<a_j,则 lisjlisi+1lis_j\ge lis_i+1
  3. ai=aja_i=a_j,则不可能。(a为排列)

因此,对于某个排列生成的所有二元组,其必定是两两不同的。

得证所有二元组中的最大值至少为 n⌈\sqrt{n}⌉

由上述结论,我们可以将整个序列分为若干组,每组 n⌈\sqrt{n}⌉ 块,将每组内元素倒序输出( max{lds}nmax\{lds\}\le ⌈\sqrt{n}⌉ ),各组正序输出( max{lis}nnnmax\{lis\}\le ⌈\frac{n}{⌈\sqrt{n}⌉}⌉\le ⌈\sqrt{n}⌉ )即可。

代码

#include <stdio.h>
#include <math.h>
#include <algorithm>
using namespace std;
int T,n;
int main()
{
	for (scanf("%d",&T);T--;)
	{
		scanf("%d",&n);
        	int k=sqrt(n),i;
        	if (k*k!=n) k++;
        	for (i=0;i<=n;i+=k)
            		for (int j=min(n,i+k);j>i;--j) printf("%d ",j);
		printf("\n");
	}
        return 0;
}