操作が「Euclid の互除法」っぽくなっている系の問題!!!そういう系の問題は次の一覧で示してる
問題概要
個の正の整数
に対して次の操作を繰り返し行う。
個の整数の最大値を
、最小値を
とする。
なら手続きを終了し、そうでなければ
が書かれたカードをすべて
が書かれたカードに変える
この問題の制約下で、いずれ手続きが終了することが証明できる。手続き終了後の数値を求めよ。
制約
考えたこと
まず の場合を考える。このとき、たとえば
とすると
- 54 を 54 - 15 = 39 に置き換えて (15, 39) となる
- 39 を 39 - 15 = 24 に置き換えて (15, 24) となる
- 24 を 24 - 15 = 9 に置き換えて (15, 9) となる
- 15 を 15 - 9 = 6 に置き換えて (6, 9) となる
- 9 を 9 - 6 = 3 に置き換えて (6, 3) になる
- 6 を 6 - 3 = 3 に置き換えて (3, 3) になる
という具合に、答えは 3 になる。上記の手続きを振り返ると、そもそも最初に 54 を 15 で割ったあまりで置き換えていることがわかる。このような操作は Euclid の互除法の各ステップとみなせる。よって最終的に生成される整数は「最大公約数」となる。
一般の N の場合
一般の であっても、まず次のことがいえる。
「操作を行なっても、 個の整数の最大公約数は不変である」
これは
であることからわかる。よって、シミュレーション終了時の値が「最大公約数」となることがわかった。計算量は となる。
なお、シミュレーションがかならず終了することは次のようにしてわかる。
とならない限り、0 が生成されることはない
- したがって
個の整数の総和は
以上の範囲を動く
- したがって
- 操作を行うたびに「
個の整数の総和」は減少する
ことから、無限回の操作を行うことはできず、いつかは操作が終了する。
#include <bits/stdc++.h> using namespace std; long long GCD(long long a, long long b) { if (b == 0) return a; else return GCD(b, a % b); } int main() { int N; cin >> N; long long res = 0; for (int i = 0; i < N; ++i) { long long A; cin >> A; res = GCD(res, A); } cout << res << endl; }