EF161 E. Increasing Subsequences

传送门Problem - E - Codeforces

题意:给定一个整数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个。

这样算法处理时可以先把空集拿掉,剩下的数字用2^{p}-1来拆分,比如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;
}

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值