【51Nod1454】升排列

40 篇文章 0 订阅

定义长度为n的排列为数组 p = [p1, p2, …, pn] ,这个数组包含n个整数,他们都在1到n之间,并且两两不同。我们说这个排列把1映射到 p1 ,2映射到 p2 ,依此类推。
下面介绍一下排列的循环表示。一个环是一串数字,这一串数字中每一个数字被映射到下一个数字,最后一个数字被映射到第一个数字。排列p的循环表示是由一系列的环构成的。比如排列p = [4, 1, 6, 2, 5, 3]的循环表示是 (142)(36)(5),因为1映射到4,4映射到2,2映射到1,3和6相互映射,5是自己映射自己。
排列可能会有多个循环表示,所以这儿再定义一个标准的循环表示。第一步,把每一个环中的数字按照降序排列。然后把每一个环按照环中最大的数字升序排列。对于上面的例子, [4, 1, 6, 2, 5, 3]的标准循环表示是(421)(5)(63)。
把环旁边的圆括号拿掉之后我们会得到另外一个排列,比如[4, 1, 6, 2, 5, 3] 会得到 [4, 2, 1, 5, 6, 3]。
一些排列经过上述的变换之后,排列是不变的。现在我们把所有长度为n的满足这样的条件的排列按照字典序排列。请你找出排在第k位的是什么排列。
样例解释:标准循环表示为(1)(32)(4),拿掉括号之后就是[1 3 2 4]。第一个排列是 [1, 2, 3, 4],第二个是[1, 2, 4, 3]。

Input
单组测试数据。
第一行包含两个整数 n, k (1 ≤ n ≤ 50, 1 ≤ k ≤ min{10^18,L},L是符合条件的排列数目。
Output
输出n个以空格分开的数字,代表第k个排列。
Input示例
4 3
Output示例
1 3 2 4

题解
发现映射关系只能用两种①i+1与i②i与i
也就是说只能交换相邻两个位置,找规律发现与斐波那契数列有关。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<set>
#include<ctime>
#include<vector>
#include<cmath>
#include<algorithm>
#include<map>
#include<queue>
#define mod 20101009
#define ll long long 
#define N 10000005 
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,ans[55];
ll f[55],k;
int main()
{
    n=read();scanf("%lld",&k);
    f[n]=1;
    f[n+1]=1; 
    ans[n]=n;
    for (int i=n-1;i;i--)
    {
        f[i]=f[i+1]+f[i+2];
        ans[i]=i;
    }
    for (int i=2;i<=n&&k;i++)
    {
        if (f[i]<k)
        {
            k-=f[i];
            swap(ans[i],ans[i-1]);
            i++;
        }
    }
    for (int i=1;i<n;i++) printf("%d ",ans[i]);
    printf("%d",ans[n]);
    return 0;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值