トコロテンの日記

自分の活動内容や普段考えていることをアウトプットするためのブログです。ITに関わる話がメインであり、開発、競技プログラミング、気に入った技術などの話が多くなります。

AtCoder Beginner Contest 112 D - Partition

問題

atcoder.jp

考察と解法

まずはじめに、すべてのa_iがすべてのa_iの最大公約数で割り切れることに気づきました(それはそう)。
a_1 + a_2 + ... + a_N = Mを満たす (a_1, a_2, ..., a_N)の最大公約数を xとするとa_i \mod x = 0であるから
自然数 (b_1, b_2, ..., b_N)を用いて

b_1 x + b_2 x + ... + b_N x = M
(b_1 + b_2 + ... + b_N)x = M (式1)

となり、xはMの約数であることがわかります。
したがって、Mの約数をすべて列挙し、(式1)のxに代入して満たすか確認すればよいです。 しかし、xが最大公約数でなくても、公約数であるなら(式1)を満たすことがわかります。
例えば、N = 3, M = 14の場合を考えると

Mの約数=(1, 2, 7, 14)

(2 + 4 + 8)1 = 14
(1 + 2 + 4)2 = 14

となりx = 1, 2の2つの場合があることがわかります。 (式1)を満たすxをすべて求めて最大のものが真のxとなるため、この場合はx=2が解になります。 また、(7, 14)はMの約数でありながらx=7, 14では(式1)を満たすことはできません。 この理由は、(式1)を以下のように変形するとわかります。

(b_1 + b_2 + ... + b_N) = \frac M x

xはMの約数なので右辺は必ず整数値になります。  (b_1, b_2, ..., b_N)自然数であるという制約から

 (b_1 + b_2 + ... + b_N) \ge N
\frac M x \ge N (式2)

となり、x=7, 14では(式2)を満たさないため、条件を満たす(b_1, b_2, ..., b_N)を構成することができません。 逆に(式2)を満たすなら、(b_1, b_2, ..., b_N) = (1, 1, ..., \frac M x - N + 1)とすることで(b_1, b_2, ..., b_N)を構成することができます。

以上のことをまとめると以下のアルゴリズムでこの問題を解くことができます。

アルゴリズム

  1. Mのすべての約数を列挙する
  2. Mの約数の中から(式2)を満たすxをすべて調べる
  3. 2で調べたxの中で最大のものを解とする

計算量

1の処理がO(\sqrt M)
2の処理が多く見積もってO(2\sqrt M)
3は2を行う中で最大値を更新すればよいため、O(1)

1, 2の処理は独立しているため、全体の計算量はO(\sqrt M)

ソースコード

#include <iostream>
#include <vector>
#include <algorithm>
 
using namespace std;
 
int main()
{
    int n, m, gcd = 1;
    vector<int> divisor;
 
    cin >> n >> m;
 
    for(int i = 1; i * i <= m; ++i)
    {
        if(m % i == 0)
        {
            divisor.push_back(i);
            divisor.push_back(m / i);
        }
    }
 
    for(int i = 0; i < divisor.size(); ++i)
    {
        if(m / divisor[i] >= n)
        {
            gcd = max(gcd, divisor[i]);
        }
    }
 
    cout << gcd << endl;
}

感想

数学っぽい問題が本当に苦手なので解けて嬉しいです。