1 条题解
-
0
问题理解
我们有一个排列 ( p[0], p[1], \dots, p[n-1] ),它是 ( 0 ) 到 ( n-1 ) 的一个排列。
我们只能选择一些行星来加速,并且选择的行星对应的轨道速度必须严格递增。
也就是说,选择的索引集合 ( i_1 < i_2 < \dots < i_m ) 必须满足 ( p[i_1] < p[i_2] < \dots < p[i_m] )。
这就是排列的递增子序列(不要求连续)。题目要求:
给定 ( k ),构造一个长度 ( n ) 尽量小的排列,使得它的递增子序列个数恰好等于 ( k )。
关键思路
1. 空子序列
递增子序列包括空序列,所以如果排列的递增子序列总数为 ( k ),那么非空子序列数是 ( k-1 )。
但题目中给的 ( k ) 是包含空序列的(从样例可以看出:( k=3 ) 时,子序列有[], [0], [1]共 3 个)。
2. 已知结论
一个常见的构造方法是利用二进制分解或斐波那契分解来构造排列,使得递增子序列数恰好为 ( k )。
一种简单的方法:
- 考虑在排列末尾添加一个比之前所有数都大的数,那么递增子序列数会翻倍(因为所有原来的子序列都可以加上这个新数,形成新的递增子序列)。
- 考虑在排列末尾添加一个比之前所有数都小的数(比如 0),那么递增子序列数会加 1(因为只有这个新数单独作为一个子序列,以及空序列加上它)。
3. 构造方法(二进制法)
假设 ( k ) 的二进制表示是 ( 1b_{m-1}b_{m-2}\dots b_1 )(最高位是 1)。
我们从空排列开始,递增子序列数初始为 1(空序列)。从左到右处理二进制位(从最高位之后的位开始):
- 如果当前位是
1:先执行“翻倍”操作(加一个最大数),再执行“加 1”操作(加一个最小数)。 - 如果当前位是
0:只执行“翻倍”操作。
这样,每个
1对应“×2+1”,每个0对应“×2”。
例子:
( k = 6 )(二进制110)- 初始:
[],子序列数 = 1(空序列) - 二进制
110,从第二位开始:- 第二位
1:翻倍(加最大数0) →[0],子序列数=2;再加 1(加最小数1到前面) →[1,0],子序列数=3。 - 第三位
0:翻倍(加最大数2) →[1,0,2],子序列数=6。
- 第二位
检查
[1,0,2]的递增子序列:- 空
[] [1][0][2][0,2][1,2]共 6 个,正确。
4. 更优的构造(使用最长递增子序列 LIS 计数技巧)
实际上,更优的方法是使用一种排列,它的递增子序列数可以表示为 2 的幂次乘积。
例如:- 排列
[0]:子序列数 = 2([], [0]) - 排列
[0,1]:子序列数 = 4([], [0], [1], [0,1]) - 排列
[1,0]:子序列数 = 3([], [0], [1])
我们可以把 k 分解成 ( k = 2^{a_1} + 2^{a_2} + \dots ) 的形式,然后构造对应块。
块构造法:
- 一个长度为 ( m ) 的递增序列(如
0,1,...,m-1)的递增子序列数是 ( 2^m )(每个元素选或不选)。 - 一个长度为 ( m ) 的递减序列(如
m-1, m-2, ..., 0)的递增子序列数是 ( m+1 )(只能选一个元素或空序列)。 - 我们可以把 k 写成二进制,每一位对应一个块,块之间用“隔断”方式保证不跨块形成新的递增子序列。
具体做法:
- 将 k 写成二进制,设最高位是 ( 2^m )。
- 构造一个
0,1,...,m-1作为第一个块(子序列数 ( 2^m ))。 - 对剩下的位数(从高到低),如果某位为 1,就加一个递减序列块,长度等于该位代表的幂次,并确保这个块的所有数大于之前所有数,这样不会和前面形成跨块递增子序列,但块内部贡献 ( \text{长度}+1 ) 的子序列数。
- 最后排列的总递增子序列数就是各块贡献的乘积,等于 k。
5. 长度优化
这种方法得到的长度大约是 ( O(\log k) ) 级别,通常很短,满足题目要求。
总结
题解的核心是:
- 理解排列的递增子序列计数规律。
- 利用二进制分解,将 k 拆成若干 2 的幂次组合。
- 用递增块(贡献 2 的幂)和递减块(贡献幂次+1)拼接构造。
- 确保块之间数字大小关系,避免跨块产生额外递增子序列。
这样就能在 ( n \le 120 ) 内构造出符合要求的排列,满足题目约束。
- 1
信息
- ID
- 4281
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 11
- 已通过
- 1
- 上传者