前言
现在是22:10,离比赛结束过去了10分钟。
题目
题目大意:
给一个目标正整数 (N),你初始有两个数 (x=0,y=0) ,有四种操作:
- 将 (x) 替换为 (x+1)。
- 将 (y) 替换为 (y+1)。
- 将 (x) 替换为 (x+y)。
- 将 (y) 替换为 (x+y)。
要求在 (130) 次操作内将 (x) 变为目标数 (N)。
(1le Nle 10^{18}.)
讲解
先放一个大数镇楼:679891637638612257。
我们先随便玩一玩发现,为了达到较大的目标状态,我们肯定需要反复使用操作 (3,4) 。
我反复用的操作顺序是 (4,3)。
假设初始状态为 ((1,1)),那么之后的状态为:((1,2),(3,2),(3,5),(8,5),(8,13)...)
很显然的斐波拉契数列。
自然我们想到将 (N) 拆分为多个斐波拉契数相加,在适当的时机加上 ((1,1)),当然根据斐波拉契数位置的奇偶性不同,我们也可能要加上 ((2,1))。
也就是说我们将 (x) 和 (y) 也拆分为多个斐波拉契数。
比如我们看这个例子:(N=11=8+3)。
((0,0;0,0),(0,1;0,1),(0,1;0,2),(0,3;0,2),(1,3;1,2),(1,3;2,5),(3,8;2,5).)
分号前为 (x),之后为 (y)。
最终状态:(x=8+3=11,y=2+5=7.)
到这里,我们的思路已经基本成型,但是这样还不够,细节上还需要优化,不然会被卡。
比如我们并不需要加上 ((1,1)),因为 ((0,1)) 经过一轮 (4,3) 后可以变成 ((1,1)),((1,0)) 经过一轮 (4,3) 后可以变成 ((2,1))。单独加 ((1,1)) 或 ((2,1)) 显然没有一轮 (4,3) 操作优,因为 (4,3) 操作是共用的。
详见代码。
代码
//12252024832524
#include <cstdio>
#include <cstring>
#include <algorithm>
#define TT template<typename T>
using namespace std;
typedef long long LL;
const int MAXN = 100005;
LL n,f[MAXN];
LL Read()
{
LL x = 0,f = 1;char c = getchar();
while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
return x * f;
}
TT void Put1(T x)
{
if(x > 9) Put1(x/10);
putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
if(x < 0) putchar('-'),x = -x;
Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}
int c[MAXN],tot,ans[MAXN],anstot;
void Add(int x){ans[++anstot] = x;}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = Read();
f[1] = 1; f[2] = 2;
for(int i = 3;i <= 88;++ i) f[i] = f[i-1] + f[i-2];
for(int i = 88;i >= 1;-- i) if(n >= f[i]) c[++tot] = i,n -= f[i];
for(int i = 1;i <= tot;++ i)
{
if(i == tot)
{
if(c[tot] & 1) Add(2);
else Add(1);
for(int j = 1;j <= (c[tot]+1) / 2;++ j) Add(4),Add(3);
}
else
{
if(c[i] & 1) Add(2);
else Add(1);
for(int j = 1;j <= ((c[i]+1)/2-(c[i+1]+1)/2);++ j) Add(4),Add(3);
}
}
Put(anstot,'
');
LL x = 0,y = 0;
for(int i = 1;i <= anstot;++ i)
{
Put(ans[i],'
');
if(ans[i] == 1) x++;
else if(ans[i] == 2) y++;
else if(ans[i] == 3) x += y;
else y += x;
}
Put(anstot,'
');
return 0;
}
//679891637638612257
后记
赛时因为造了一个大于 (10^{18}) 的数,操作数达到 (132) 而怀疑人生。
不过赛时人确实有点麻了,想什么都不清楚,迷迷糊糊地就开始敲代码,结果WA到怀疑人生。
倦了。