B - 細胞の培養 / Cell Cultivation Editorial by admin
Gemini 3.0 Flash概要
初期個数 \(S\) の細胞を \(1\) 回ごとに \(2\) 倍に増やす「分裂促進」操作を行い、ちょうど \(T\) 個にするために必要な最小の操作回数を求める問題です。ただし、操作回数は最大 \(K\) 回までという制限があります。
考察
この問題のポイントは、細胞が「常に \(2\) 倍ずつ増える」という性質に注目することです。
倍率の確認 操作を \(n\) 回行うと、細胞の数は \(S \times 2^n\) 個になります。 したがって、目標の \(T\) 個にするためには、まず \(T\) が \(S\) で割り切れること が必須条件です。もし
T % S != 0であれば、どのような操作を行ってもちょうど \(T\) 個にすることはできないため、即座に-1と判定できます。2の累乗の判定 \(T\) が \(S\) で割り切れる場合、その倍率 \(ratio = T / S\) が \(2\) の累乗(\(2^0, 2^1, 2^2, \dots\)) になっている必要があります。 例えば:
- \(S=3, T=12\) のとき、倍率は \(4\) です。\(4 = 2^2\) なので、操作 \(2\) 回で到達可能です。
- \(S=3, T=9\) のとき、倍率は \(3\) です。\(3\) は \(2\) の累乗ではないため、到達不可能です。
操作回数の制約 倍率が \(2^n\) と表せるとき、必要な操作回数は \(n\) 回です。この \(n\) が問題で与えられた制限 \(K\) 以内(\(n \leq K\))であれば \(n\) を出力し、そうでなければ到達不能として
-1を出力します。
アルゴリズム
- \(T\) が \(S\) で割り切れるかチェックします。
- 割り切れる場合、倍率 \(ratio = T // S\) を計算します。
- \(ratio\) が \(2\) の累乗であるかを確認します。
- ビット演算の性質を利用すると、
ratio > 0かつ(ratio & (ratio - 1)) == 0であれば \(2\) の累乗です。 - あるいは、
ratioを \(2\) で割り続けて確認することも可能です。
- ビット演算の性質を利用すると、
- \(ratio = 2^n\) となる \(n\) を求めます。
- Pythonでは
ratio.bit_length() - 1を使うことで、その数値が \(2\) の何乗であるかを効率的に取得できます。
- Pythonでは
- \(n \leq K\) かどうかを判定し、結果を出力します。
計算量
- 時間計算量: \(O(1)\) もしくは \(O(\log(T/S))\)
- 数値のビット長に基づいた計算やビット演算は非常に高速です。\(T, S \leq 10^{18}\) ですが、高々 \(60\) 回程度のループやビット演算で完了します。
- 空間計算量: \(O(1)\)
- 入力値以外に保持するデータは定数個です。
実装のポイント
大きな数の扱い: \(S, T\) は最大 \(10^{18}\) と非常に大きいため、言語によっては 64ビット整数型(C++の
long longなど)を使用する必要があります。Pythonでは標準で大きな整数を扱えるため、そのまま計算可能です。ビット演算の活用:
ratio & (ratio - 1)は、コンピュータ科学において「ある数が \(2\) の累乗かどうか」を判定する際によく使われるテクニックです。特殊なケース: \(S = T\) のとき、倍率は \(1\) (\(2^0\)) となり、必要な操作回数は \(0\) 回です。このケースも上記のアルゴリズムで正しく処理できます。
ソースコード
import sys
def solve():
# 入力を読み込む
line = sys.stdin.readline()
if not line:
return
try:
s, t, k = map(int, line.split())
except ValueError:
return
# TがSで割り切れない場合は、2倍し続けてTにすることは不可能
if t % s != 0:
print("-1")
return
# 倍率を計算
ratio = t // s
# 倍率が2の累乗であるかを確認する
# ratio & (ratio - 1) == 0 かつ ratio > 0 であれば2の累乗
if ratio > 0 and (ratio & (ratio - 1)) == 0:
# 2の何乗であるかを求める
# bit_length() は 2^n のとき n + 1 を返す
n = ratio.bit_length() - 1
# 操作回数がK回以内であればその回数を出力
if n <= k:
print(n)
else:
print("-1")
else:
# 2の累乗でない場合は不可能
print("-1")
if __name__ == '__main__':
solve()
この解説は gemini-3-flash-preview によって生成されました。