ARC122C Calculator 题解
一、题目:
二、思路:
这道题又是个妙题。
先看一个定理:齐肯多夫定理。
任何正整数都可以表示成若干个不连续的斐波那契数(不包括第一个斐波那契数)之和。这种和式被称为齐肯多夫表述法。
那么怎样找出齐肯多夫表述法呢?
对于任何正整数,其齐肯多夫表述法都可以由贪心算法(即每次选出最大可能的斐波那契数)得到。
那么齐肯多夫定理和这道题有什么关系吗?
首先,如果不用操作1和操作2,交替使用操作3和操作4,得到的序列一定是斐波那契数列。我们考虑在这个操作序列中插入一些操作1和操作2,使得最终凑出正整数 \(n\) 来。
具体来说,我们首先求出 \(n\) 的齐肯多夫表述法,并将选中的斐波那契数打上标记。设最大的选中的斐波那契数是第 \(sz\) 个。
-
依次从大到小扫描每个斐波那契数,即扫描 \(fib(sz)\sim fib(2)\)。
-
设当前扫描到了 \(fib(i)\)。
- 若 \(fib(i)\) 被打过标记,则根据 \(i\) 的奇偶性,输出操作1或者输出操作2.
- 否则,什么都不做。
根据 \(i\) 的奇偶性输出操作3或者输出操作4.
考虑这样为什么是正确的。假设我们当前在某个位置加了1,那么这个1到最后就会演变成一个斐波那契数。而这些1的演变过程是互不干扰的。
三、代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define FILEIN(s) freopen(s".in", "r", stdin);
#define FILEOUT(s) freopen(s".out", "w", stdout)
#define mem(s, v) memset(s, v, sizeof s)
inline long long read(void) {
long long 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 f * x;
}
const int maxn = 100;
int sz;
long long f[maxn], n;
int opt[maxn], tot;
bool tag[maxn];
int main() {
n = read();
if (n == 1) {
printf("1\n1\n");
return 0;
}
f[1] = 1; f[2] = 1;
for (int i = 3; ; ++ i) {
f[i] = f[i - 1] + f[i - 2];
if (f[i] > n) {
sz = i - 1; break;
}
}
{
int i = sz;
while (n) {
if (n >= f[i]) { n -= f[i]; tag[i] = true; }
-- i;
}
}
for (int i = sz; i >= 2; -- i) {
if (tag[i]) {
if (i % 2 == 0) opt[++ tot] = 2;
else opt[++ tot] = 1;
}
if (i % 2 == 0) opt[++ tot] = 3;
else opt[++ tot] = 4;
}
printf("%d\n", tot);
for (int i = 1; i <= tot; ++ i)
printf("%d\n", opt[i]);
return 0;
}