> makibishi throw|

ABC136E - Max GCD

問題概要

リンク

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

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

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

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

制約

2≤N≤5002 \leq N \leq 500

1≤Ai≤1061 \leq A_i \leq 10^{6}

0≤K≤1090 \leq K \leq 10^{9}

解法

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

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

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

加工後の整数列をA1′,A2′,⋯AN′A'_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が成り立つ。

また、全てのAi′A'_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を用意した時、全てのAi′A'_iをxxの倍数にするために何回操作が必要なのかを考える。これが分かれば、解の候補を大きい順に見ていき、KK回以下の操作で作ることができることが判明したらその時のxxが求める答えとなる。

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

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

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

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

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

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

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

が成り立つ。このbbは∑i=1NAimodx\sum_{i=1}^{N} A_i {\rm mod} xをxxで割ることで求めることができる。

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

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

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

まとめ

∑i=1NAi\sum_{i=1}^{N} A_iの約数が解の候補となる。解の候補である各xxに対し、KK回の操作で各Ai′modx=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個のx−Aimodxx -A_i {\rm mod} xを足し合わせたものが必要な操作の回数になる。

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