题意:给定一个整数X,构造一个序列长度不超过200,序列的上升子序列个数恰好为X。
例如序列[1,2],有4个上升子序列,分别是空序列[],[1],[2],[1,2]。
方法1(无法通过):根据二进制的知识,一个整数可以拆分成2进制和的形式。因此首先想到拆分成二进制的和,再由大到小构造连续的子序列。
和基本二进制处理略有区别,比如序列[3,4,1,2]包含2个连续子序列,但所有子序列只有一个空集,答案是4+3=7个上升子序列,再看看这个[7,8,9,3,4,5,1,2],共有3个连续子序列,因为后面的数字小,不可能和前面关联起来,所以子序列个数为[7,8,9]的8个,[3,4,5]的7个,[1,2]的3个。
这样算法处理时可以先把空集拿掉,剩下的数字用来拆分,比如20可以-1为19,19可以拆分为15+3+1,这样构造有三段的连续子序列[8,9,10,11, 5,6, 1]。
此方法可以得到满足条件子序列,但无法通过OJ,因为最终子序列长度会超过200。代码如下:
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
long long t,n,pw[62]={1},ans[205];
int main()
{
for(int i=1;i<=62;i++)/**< 提前构造2的p次幂数组 */
pw[i]=pw[i-1]*2;
cin>>t;
while(t--)
{
cin>>n;
for(int i=0;i<=200;i++)/**< 逆序数组,可以确保前面数字更大 */
ans[i]=200-i;
n=n-1;/**< 空集存在,先-1 */
int p=62,len=0;
while(n)
{
if(n>=pw[p]-1) /**< 长度为p的连续序列上升子序列个数为2^p-1 */
{
reverse(ans+len,ans+len+p);/**< 翻转数组这一段 */
n-=pw[p]-1;
len+=p;
}
else
p--;
}
cout<<len<<endl;
for(int i=0;i<len;i++)
cout<<ans[i]<<' ';
cout<<endl;
}
return 0;
}
方法2(正解):官方题解,如果在一个能构成X子序列的序列后面加一个最小值,那么新序列能构成X+1个子序列,如果加一个最大值,那么能构成2X个子序列。按此方法递归思想处理,当我们需要得到一个X子序列时,
(1)如果X是一个奇数,先在序列尾部插入一个极小值,那么问题就变成了如果构造一个长度为X-1的子序列。
(2)如果X是一个偶数,先在序列尾部插入一个极大值,那么问题就变成了如果构造一个长度为X/2的子序列。
#include <bits/stdc++.h>
using namespace std;
long long t,n,pw[62]={1},ans[205];
int main()
{
cin>>t;
while(t--)
{
vector<int>ans;
int minv=-100,maxv=100;/**< 初始给定极小极大值 */
cin>>n;
while(n>=2)
{
if(n%2==1)
ans.push_back(minv),minv++,n--;
else
ans.push_back(maxv),maxv--,n/=2;
}
cout<<ans.size()<<endl;/**< 逆向输出 */
for(int i=ans.size()-1;i>=0;i--)
cout<<ans[i]<<' ';
cout<<endl;
}
return 0;
}