题目链接:https://atcoder.jp/contests/arc122/tasks/arc122_c
有两个变量 \(x,y\),初始时均为 \(0\)。
可以执行以下操作不超过 \(130\) 次:
给定一个数字 \(n\),要求给出一个操作序列使得依次操作后 \(x\) 的值等于 \(n\)。
\(n\leq 10^{18}\)。
我们发现操作 \(3,4\) 其实就是在计算斐波那契数列。而众所周知任意一个正整数 \(n\) 都可以被拆分为若干个不同的斐波那契数的和,所以考虑用斐波那契数的和凑出 \(n\)。
如果 \(n\) 就是斐波那契数列中的一项,那么只需要先进行一次操作 \(1\),然后不断进行操作 \(3,4,3,4\) 即可。
如果 \(n\) 等于斐波那契数列的第 \(a_1,a_2\cdots a_k(a_1<a_2<\cdots<a_k)\) 项之和,那么我们先按照上述方法构造出一个序列使得 \(x=a_k\),然后在倒数第 \(a_i(1\leq i<k)\) 次操作后面加入一个 \(1\) 或 \(2\)(往 \(x\) 和 \(y\) 中大的那一个上面加),那么最终的结果就是 \(a_1+a_2+\cdots+a_k=n\) 了。
例如 \(17=1+3+13\),构造方法如下:
注意有可能操作后是 \(y\) 为 \(n\),此时把操作取反一下即可。
操作次数的话,斐波那契数列第 \(88\) 项超过了 \(10^{18}\),所以操作 \(3,4\) 的次数最多是 \(87\),操作 \(1,2\) 的次数为 \(n\) 拆分成的斐波那契数的数量,由于不可能拆分成相邻的两个斐波那契数(他们的和等于下一个斐波那契数),且斐波那契数列第一项和第二项都是 \(1\),所以至多拆分为 \(\text{fib}_2+\text{fib}_4+\cdots+\text{fib}_{86}\),一共 \(43\) 项。所以操作次数上界为 \(87+43=130\)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100;
const ll Inf=1e18;
ll n,m,p,fib[N];
queue<int> q;
vector<int> a;
int main()
{
scanf("%lld",&n);
fib[1]=1; p=n;
for (m=2;fib[m-1]<=n;m++)
fib[m]=fib[m-1]+fib[m-2];
for (int i=m;i>=1;i--)
if (p>=fib[i]) p-=fib[i],a.push_back(i);
a.push_back(2);
int v=(!(a[0]&1))+3;
for (int i=1;i<a.size()-1;i++)
{
q.push(5-v);
for (int j=a[i];j>a[i+1];j--)
q.push(v),v=7-v;
}
q.push(3);
cout<<q.size()<<"\n";
for (;q.size();q.pop())
cout<<q.front()<<"\n";
return 0;
}
原文:https://www.cnblogs.com/stoorz/p/14879414.html