正题
题目链接:https://atcoder.jp/contests/arc122/tasks/arc122_c
题目大意
一个数对开始是\((0,0)\),每次可以选择一个数加一或者让一个数加上另一个数,求使得第一个数变成\(n\)的方案。步数不超过\(130\)。
\(1\leq n\leq 10^{18}\)
解题思路
官方是斐波那契,但是我考试的时候过法比较神奇。
看上去比较像更相减损,但是不知道第二个数是多少,感觉我们应该能找到一个数对\((n,k)\)使得它更相减损的步骤很少。
考虑的分散一点,设\(n=xk+k\)。那么我们相当于要找到一个\(x\)使得\(\frac{xk+k}{k}=\frac{1}{x}\)。
然后解出来得到\(x=\frac{\sqrt 5-1}{2}\)(其实就是黄金分割率)。
所以我们让\(k=n\times \frac{\sqrt 5-1}{2}\)然后更相减损下去,之后会发现有点误差,我们前后各枚举\(10000\)找到一个满足条件的就好了。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#define ll long long
using namespace std;
ll n;vector<ll> z;
void print(ll x,ll y){
if(!x){while(y&&z.size()<=130)y--,z.push_back(2);return;}
if(!y){while(x&&z.size()<=130)x--,z.push_back(1);return;}
if(x<y) print(x,y-x),z.push_back(4);
else print(x-y,y),z.push_back(3);
}
signed main()
{
scanf("%lld",&n);
double M=(sqrt(5.0)-1.0)*(double)n/2.0;
ll m=M;
for(ll i=max(m-10000,0ll);i<=m+10000;i++){
print(n,i);
if(z.size()>130){z.clear();continue;}
printf("%lld\n",z.size());
for(ll i=0;i<z.size();i++)
printf("%lld\n",z[i]);
break;
}
return 0;
}