ARC122C Calculator 题解

一、题目:


二、思路:

这道题又是个妙题。

先看一个定理:齐肯多夫定理

任何正整数都可以表示成若干个不连续的斐波那契数(不包括第一个斐波那契数)之和。这种和式被称为齐肯多夫表述法

那么怎样找出齐肯多夫表述法呢?

对于任何正整数,其齐肯多夫表述法都可以由贪心算法(即每次选出最大可能的斐波那契数)得到。


那么齐肯多夫定理和这道题有什么关系吗?

首先,如果不用操作1和操作2,交替使用操作3和操作4,得到的序列一定是斐波那契数列。我们考虑在这个操作序列中插入一些操作1和操作2,使得最终凑出正整数 \(n\) 来。

具体来说,我们首先求出 \(n\) 的齐肯多夫表述法,并将选中的斐波那契数打上标记。设最大的选中的斐波那契数是第 \(sz\) 个。

  1. 依次从大到小扫描每个斐波那契数,即扫描 \(fib(sz)\sim fib(2)\)

  2. 设当前扫描到了 \(fib(i)\)

    1. \(fib(i)\) 被打过标记,则根据 \(i\) 的奇偶性,输出操作1或者输出操作2.
    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;
}
posted @ 2021-06-13 14:33  蓝田日暖玉生烟  阅读(173)  评论(0编辑  收藏  举报