Codeforces Round #613 (Div. 2) F - Classical?

問題概要

$$n$$ 個の整数からなる数列 $$A$$ が与えられるので、$$\mathrm{lcm} (A_i, A_j)$$の最大値を求めよ。

(問題では添字が相異なるという条件がついていますが、$$\mathrm{lcm} (a, a) = a \leq \mathrm{lcm} (a, x)$$なので問題ありません。)

  • $$ 2 \le n \le 10^{5} $$
  • $$ 1 \le A_{i} \le 10^{5} $$

codeforces.com

問題を変形する

まず、LCMは扱いづらいので、$$ \displaystyle \mathrm{lcm} (a, b) = \frac{a}{\gcd (a, b) } \cdot b $$ と変換します。

このとき、$$ \displaystyle \frac{a}{\gcd (a, b) } $$と $$ b $$ は互いに素になるので、あらかじめ数列に$$ a $$ の約数全てを加えておくことにすると、次のような問題に帰着できます。

  • 互いに素な要素2つの積の最大値を求めよ。

これでだいぶ見通しがよくなりました。

うまく候補を絞る

2つの要素を選んで何かを求める問題では、片方を全探索すると解ける場合が多いです。このテクニックはこの問題でも有効で、小さい方を全探索するとよいです。

以下、$$A$$は昇順に並んでいるとします。

$$A_i$$を$$i$$の大きい方から見ていきます。まずは愚直に探索してみると、次のようになります。

$$A_j \ (i < j)$$を全探索し、$$\gcd(A_i, A_j) = 1$$ ならば $$A_i A_j$$で答えを更新する。

これは当然間に合いませんが、$$A_i$$を降順に見ているため、次のループから$$A_j$$として見るのはこの次の要素からでよいことがわかります。

しかしこれでも、互いに素な要素がない場合に、すべての組を見ることになってしまい間に合いません。

包除原理で高速化

これを高速化するためには、次のクエリに高速に答えられればよいです。

  • 数列に xを追加する。
  • 数列から xを削除する。
  • 数列のうち、 xと互いに素な要素の数を求める。

これは、包除原理を使えば解くことが出来ます。「互いに素」の余事象を考えると、「$$x$$と共通な素因数を少なくとも1つ持つ」となります。

$$x$$が持つ素因数を$$p_1, p_2, \ldots , p_k $$とすると、上の3つのクエリは、$$ \lbrace p_1, p_2, \ldots , p_k \rbrace $$のすべての部分集合$$S$$について、

  • 追加/削除のとき、$$cnt [Sの要素の総積]$$に1を加算/減算する
  • 求値クエリのとき、$$ (-1)^{|S|} cnt [Sの要素の総積] $$の総和を求める

とすれば、$$2 \times 3 \times 5 \times 7 \times 11 \times 13 \times 17 = 510510 \geq 10^{5} $$より、異なる素因数の個数は6以下なので、十分高速です。

ACコード

codeforces.com

備考

包除原理パートは、次の問題が参考になります。(というよりも、ほぼ同じです) codeforces.com