Processing math: 6%

目次

AtCoder Beginner Contest 349 F問題メモ

AtCoder Beginner Contest 349

F - Subsequence LCM

F - Subsequence LCM

問題

解法1

最大公約数を考えるには、まず M を素因数分解する。
M \le 10^{16} なので O(\sqrt{M}) の素因数分解はやや厳しく見えるが、演算が単純なのでできる。
不安なら確率的になるが高速なポラード・ロー法を使うこともできる。

たとえば M=1176=2^{3}3^{1}7^{2} とできる場合、

これさえ満たせば、最小公倍数が M になる。
すると、少し特殊なナップサック問題みたいに言い換えられる。

まず、M の素因数の種類数を K(今回は 3)として、各 A_i をもとに長さ K の bitflag B_i を作る。

すると、「B_i からいくつかを選んで、その bitwise-or が長さ K111…1 となるような選び方の個数」を数えればよいことになる。

このとき、2 \times 3 \times 5 \times 7 \times ... のように 素数を小さい方からかけると 14 個で 10^{16} を超えるので、素因数の種類数は最大でも K \le 13 となる。

2^{13}=8192 なので、普通にナップサックDPすると O(N 2^K) \simeq 1.6 \times 10^9 となりダメだが、
種類数が少ないことを利用して B_i が同じものはまとめて遷移させると O(4^K) \simeq 6.7 \times 10^7 で求められる。

Python3

解法2

bitwise-or のところまでは解法1と同じだが、ゼータ変換・メビウス変換を使えばナップサックより高速に求められる。

Bi ごとに、個数をカウントした配列
  000  001  010  011  100  101  110  111
[   3    2    0    1    2    3    0    0 ]

↓ゼータ変換: 
    たとえば 110 の値は、Bi=000,010,100,110 のように110の部分集合であるBiの個数の和

  000  001  010  011  100  101  110  111
[   3    5    3    6    5   10    5   11 ]

↓2の冪乗にする:
    たとえば 110 の値は、bitwise-orが110の部分集合となるようなBiの選び方の個数

  000  001  010  011  100  101  110  111
[   8   32    8   64   32 1024   32 2048 ]

↓メビウス変換:
    たとえば 110 の値は、bitwise-orが "ちょうど" 110となるようなBiの選び方の個数

  000  001  010  011  100  101  110  111
[   8   24    0   32   24  968    0  992 ]

よって、この例の答えは 992 となる。