けんちょんの競プロ精進記録

競プロの精進記録や小ネタを書いていきます

全国統一プログラミング王決定戦 エキシビジョン F - コラッツ問題 (300 点)

一発芸問題

問題へのリンク

問題概要

整数  X に対し、

  •  X \ge 10^{16} のとき  f(X) = 0
  • それ以外で  X \le 1 のとき  f(X) = 0
  • それ以外で  X が偶数のとき  f(X) = f(X/2) + 1
  • それ以外で  X が奇数のとき  f(X) = f(3X+1) + 1

で定義される。

整数  P が与えられます。 f(N) = Pとなるような  10^{16} 以下の正整数  N をいずれか 1 つ出力してください。

ただし、 f(1789997546303) = 1000 であることがわかっている。

制約

  •  0 \le P \le 1000

解法

 X になにを入れても、いつかは

  •  f(10^{16} 以上)
  •  f(1 以下)

に行き着いて、そこで 0 となり、そこから再帰を戻って行って、1, 2, 3, ... となって行く。

例えば

 f(3)
 = f(10) + 1
 = f(5) + 2
 = f(16) + 3
 = f(8) + 4
 = f(4) + 5
 = f(2) + 6
 = f(1) + 7
 = 7

という具合になる。ここからわかることは、 f(3) = 7 だったら  f(10) = 6 だし  f(5) = 5 だし...ということ。

つまり我々は  f(1789997546303) = 1000 という情報を得ているので、これを利用して、 a = 1789997546303 として、

  • a が偶数だったら 2 で割る
  • a が奇数だったら 3 をかけて 1 を足す

という操作を順々に行っていくと、 P = 999, 998, 997, ... のときの解が得られるものと考えることができる。

#include <iostream>
using namespace std;

long long Next(long long n) {
    if (n % 2 == 0) return n/2;
    else return n*3 + 1;
}

int main() {
    int P; cin >> P;
    long long start = 1789997546303LL;
    for (int i = 0; i < 1000 - P; ++i) {
        start = Next(start);
    }
    cout << start << endl;
}