描述
题解
这个题好有趣的说,不看评论区的真不知道竟然和斐波那契数列挂上钩了……
首先我们可以分析合法的序列的情况,想要变换后序列不变,那么肯定只有两种情况,要么就是 i 位置映射到 i 位置,或者就是相邻的两个 i 和 i+1 互相映射,也就是说 i 映射到 i+1, i+1 映射到 i 。
那么我们已经知道构造序列的成分如何了,但是我们怎么样才能构造出来第 k 个呢?由于根据字典序原则,我们知道随着 k 的变大,需要从右往左进行构造,但是这样子是暴力的思维,明显不成,那么问题也就来了,改变某处相邻元素的映射时会对整个序列的位次产生多少的影响呢?这个问题在讨论区有大牛提出来了,这其实是一个斐波那契数列的倒置,当我们改变第 i 和 i+1 的映射关系时,实际上是产生了 fib[i+1]+fib[i+2] 的位次的变更,这个部分不难理解但是不好想啊,如果不是大神们指出了这个,我真的无法想起来原来是斐波那契数列的关系。
所以呢,我们先将 res[] 初始化为 rank1 的状态,也就是 1、2、…、n−1、n 的序列,然后呢,预处理出来一个倒置的斐波那契数列来表示不同操作带来的位次变更幅度,接着呢,我们就是想着求出 k 是由哪些斐波那契数列值的和构成的,根据构成这个和 k 的元素来进行构造,所以这里只是一个循环就好了,这里需要注意的是,环的长度至多只有 2 ,所以每次进行交换后,都要再进行 i++,避免出现环的长度大于 2 <script type="math/tex" id="MathJax-Element-2495">2</script> 的情况哦~~~
代码
#include <iostream>
using namespace std;
typedef long long ll;
const int MAXN = 55;
int n;
int res[MAXN];
ll k;
ll fib[MAXN];
int main(int argc, const char * argv[])
{
cin >> n >> k;
fib[n] = 1;
fib[n + 1] = 0;
for (int i = n - 1; i >= 0; i--)
{
fib[i] = fib[i + 1] + fib[i + 2];
res[i] = i + 1;
}
for (int i = 0; i < n - 1 && k; i++)
{
if (k > fib[i + 1])
{
k -= fib[i + 1];
swap(res[i], res[i + 1]);
i++;
}
}
for (int i = 0; i < n; i++)
{
printf("%d ", res[i]);
}
putchar(10);
return 0;
}