> makibishi throw|

ABC136E - Max GCD

問題概要

リンク

長さNNの整数列A1,A2,ANA_1,A_2,\cdots A_NKKが与えられる。

以下の操作をKK回まで行い、整数列を加工できる。

  • 1i,jN1 \leq i,j \leq Nを満たすi,ji,jを選び、AiA_iに 1 を足し、AjA_jから 1 を引く。

この操作によって整数列を加工後、A1,...,ANA_1,...,A_Nの共通の約数で最大のものを求めよ。

制約

2N5002 \leq N \leq 500

1Ai1061 \leq A_i \leq 10^{6}

0K1090 \leq K \leq 10^{9}

解法

この問題のポイントは以下の 2 つ。

  • どのような性質を満たす数が解の候補となるか
  • 解の候補となった数が条件を満たすことをどのように判定するか

どのような性質を満たす数が解の候補となるか

加工後の整数列をA1,A2,ANA'_1,A'_2,\cdots A'_Nとし、性質を見てみる。 整数列A1,A2,ANA_1,A_2,\cdots A_Nに対して行える操作は、どこかを 1 増やし、どこかを 1 減らすことなので、数列全体の値の合計は変化しない。よってi=1NAi=i=1NAi\sum_{i=1}^{N} A_i = \sum_{i=1}^{N} A'_iが成り立つ。

また、全てのAiA'_iはこの問題の解となるある数xxの倍数になっているので、適当な数をaaとするとi=1NAi=a×x\sum_{i=1}^{N} A'_i = a \times xと表すことができる。よって、i=1NAi=i=1NAi=a×x\sum_{i=1}^{N} A_i = \sum_{i=1}^{N} A'_i = a \times xとなる。

このことから、求めたい数xxは、A1,...,ANA_1,...,A_Nの総和i=1NAi\sum_{i=1}^{N} A_iの約数の中にあることが分かった。

自然数nnの約数は、下記のような方法でO(n)O(\sqrt n)で列挙することができる。

fn enumerate_divisor(n: usize) -> Vec<usize> {
    let mut i = 1;

    let mut divisors = vec![];

    // 2乗してn以下になる数を列挙する
    while i * i <= n {
        // その数iがnを割り切る場合、iとn/iはnの約数となる
        if n % i == 0 {
            divisors.push(i);
            divisors.push(n / i);
        }
        i += 1;
    }

    divisors
}

条件を満たすことをどのように判定するか

解の候補xxを用意した時、全てのAiA'_ixxの倍数にするために何回操作が必要なのかを考える。これが分かれば、解の候補を大きい順に見ていき、KK回以下の操作で作ることができることが判明したらその時のxxが求める答えとなる。

まずは問題で定義されている操作を無視し、AiA_iに何かを足したり引いたりして最小の増減でxxの倍数に変形するためにはどうすればいいか考える。これは、Ai+dA_i + dxxで割った時の余りが 0 になるようにうまく値を調整することで下記の 2 通りのどちらかで実現できる。

  • AiA_i から何かを引いてxxの倍数にする場合は、AimodxA_i {\rm mod} xの値をAiA_iから引く。 (以後 マイナス 型と呼ぶ)
  • 逆にAiA_i に何かを足してxxの倍数にする場合は、xAimodxx - A_i {\rm mod} xAiA_iに足す。 (以後 プラス 型と呼ぶ)

このようにxxの倍数にするために必要な最小の増減量は分かったが、問題で定義されている操作と等価にするには、全てのAiA_iに対する増減の総和が 0 になっている必要がある。次はどうやってこの帳尻を合わせるのかを考える。

did_iAi+di=AiA_i + d_i = A'_iとなる数とした時、i=1Ndi=0\sum_{i=1}^{N} d_i = 0となる。 マイナス 型であればdi=Aimodxd_i = - A_i {\rm mod} x、プラス 型であればdi=xAimodxd_i = x - A_i {\rm mod} xである。マイナス 型と プラス 型の違いはxxの部分のみなので、i=1Ndi\sum_{i=1}^{N} d_iは、プラス 型を選択した回数をaaとして

i=1Ndi=a×xi=1NAimodx\sum_{i=1}^{N} d_i = a \times x - \sum_{i=1}^{N} A_i {\rm mod} x

と表されることが分かる。xxi=1NAi\sum_{i=1}^{N} A_iの約数という前提で考えると、0bN0 \leq b \leq Nとなるあるbbに対して

i=1NAimodx=b×x\sum_{i=1}^{N} A_i {\rm mod} x = b \times x

が成り立つ。このbbi=1NAimodx\sum_{i=1}^{N} A_i {\rm mod} xxxで割ることで求めることができる。

i=1Ndi=a×xb×x\sum_{i=1}^{N} d_i = a \times x - b \times xとなるため、a=ba = bとすればよい。これでプラス型を選択する回数aaを特定できた。

あとはどのiiで合計aa回のプラス型を選択するかだが、これは単にxAimodxx - A_i {\rm mod} xが小さいもの、つまりAimodxA_i {\rm mod} xが大きい順にiiを並べた時の最初のaa個とすればよい。

プラス型かマイナス型のdid_iを記録しておくことで、「1i,jN1 \leq i,j \leq Nを満たすi,ji,jを選び、AiA_iに 1 を足し、AjA_jから 1 を引く」操作の回数も分かるので、KK回以下の操作で全てのAiA'_ixxの倍数にできるかを判定できる。

まとめ

i=1NAi\sum_{i=1}^{N} A_iの約数が解の候補となる。解の候補である各xxに対し、KK回の操作で各Aimodx=0A'_i mod x = 0が構成できるかを調べる。調べる方法は下記の通り。

  • a=(i=1NAimodx)/xa = (\sum_{i=1}^{N} A_i {\rm mod} x) / xを計算し、必要なプラス 型の回数を調べる。
  • AimodxA_i {\rm mod} xが大きい順にiiを並べ、その順にaa個のxAimodxx -A_i {\rm mod} xを足し合わせたものが必要な操作の回数になる。

必要な操作の数がKK回以下の最大のxxが解となる。