E Longest Increasing Subsequence
题意:构造一个长度不超过 的排列,使得其最长上升子序列个数恰好为 个。。
解法:为了保证 LIS 的个数,一个基础的构造是 ,这样可以使得 LIS 的个数为 。同时此时的 LIS 序列长度为 。最后添加一个最大值,作为全部 LIS 的终止。接下来考虑利用二进制拆分,如何填补更小的 。
一个可行的操作是,从大到小的考虑 ,从后往前的往初始序列中插入。在第 个数字的前面插入一个比 大的数字,记为 ,这样就可以让该数字可以在前面有 种选择,同时若选择了新加入的数字,则后面只能选最大值。但是选择到了这里 LIS 的长度不够,所以需要在 的往后增补一些数字。注意到对于 的考虑,一定是在 之后考虑的。因而 这里的 LIS 长度增补可以利用后面增补的数字,只需要增补到 的长度即可。因而若 这里已经加了 个数字, 这里只需要额外增补 个数字即可。
例如,考虑 ,那么首先构造 。首先插入一个最大的:,考虑 ,因而在 前面插入一个比最大值小的数字,例如 。但是这样取到 LIS 长度不足,因而还需要增补四个数字(为了全部整数因而要将 适当下调为 ):。最后的 需要在序列的开头增补一个数字,同时为了满足 LIS 的长度, 因而需要开头加入两个数字:。最后再离散化一下就可以得到最终的排列。
这样的排列长度为:,满足条件,因为总的添加数字个数仅为 LIS 的长度。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t, m;
scanf("%d", &t);
while(t--)
{
scanf("%d", &m);
int len = 30;
while (!(m >> len & 1))
len--;
vector<int> add(len, 0);
int cnt = 0;
for (int i = len - 1; i >= 0;i--)
if (m >> i & 1)
{
add[i] = len - i - cnt;
cnt += add[i];
}
printf("%d\n", 2 * len + cnt + 1);
int base = 2 * len;
for (int i = 0; i < len;i++)
{
while (add[i] && add[i]--)
printf("%d ", ++base);
printf("%d %d ", 2 * i + 2, 2 * i + 1);
}
printf("%d\n", 2 * len + cnt + 1);
}
return 0;
}