ゼータ変換とは、
であるような
1 | 0 | 2 |
0 | 3 | 0 |
4 | 0 | 5 |
1 | 1 | 3 |
1 | 4 | 6 |
5 | 8 | 15 |
これは、
を定義すると
なので、片方の次元について累積和をした後、もう片方の次元について累積和をすると求まります。
1 | 0 | 2 |
0 | 3 | 0 |
4 | 0 | 5 |
横
1 | 1 | 3 |
0 | 3 | 3 |
4 | 4 | 9 |
縦
1 | 1 | 3 |
1 | 4 | 6 |
5 | 8 | 15 |
複数の次元で同時に累積和を取ろうとしてはいけません。
# 最初の次元についての累積和
for i in range(N-1):
for j in range(M):
B[i+1][j] += B[i][j]
# 2 つ目の次元についての累積和
for i in range(N):
for j in range(M-1):
B[i][j+1] += B[i][j]
は正しいですが、
for i in range(N):
for j in range(M):
# 最初の次元についての累積和
if i + 1 < N:
B[i+1][j] += B[i][j]
# 2 つ目の次元についての累積和
if j + 1 < M:
B[i][j+1] += B[i][j]
は WA です。(たまにやる)
素数の次元でゼータ変換をやります。
2 | 0 | 3 | 1 | 0 | 5 | 0 |
これに対し、
で定義される配列
2 | 2 | 5 | 6 | 6 | 11 | 11 |
これは、ただの累積和ですね。
1 | -1 | 4 | -2 | -4 | -3 | 2 | 3 |
これを
1 | -1 | -2 | -3 | |
4 | -4 | 3 | ||
2 |
これに対し、
で定義される配列
1 | 0 | -2 | -5 | |
5 | 0 | 1 | ||
7 |
1 | 0 | 5 | -2 | 0 | -5 | 7 | 1 |
これは、
1 | -1 | -2 | -3 | |
4 | -4 | 3 | ||
2 |
横
1 | 0 | -2 | -5 | |
4 | 0 | 3 | ||
2 |
縦
1 | 0 | -2 | -5 | |
5 | 0 | 1 | ||
7 |
1 | 0 | 1 | 0 | 0 | -1 | 1 | 1 | 0 | -1 | 1 | 0 |
これに対し、
で定義される配列
これは、
とすることで求められます。
長さ
で定義される長さ
# primes: N 以下の素数のリスト (適当な篩で用意する)
for p in primes:
for i in range(1, N // p + 1):
B[i * p] += B[i]
1 | -1 | 4 | -2 | -4 | -3 | 2 | 3 |
これに対し、
で定義される配列
逆向きに累積和をすれば良いです。
1 | -1 | -2 | -3 | |
4 | -4 | 3 | ||
2 |
横
-5 | -6 | -5 | -3 | |
3 | -1 | 3 | ||
2 |
縦
0 | -7 | -2 | -3 | |
5 | -1 | 3 | ||
2 |
これでちゃんと倍数添字の和が求まっているか確認しておきましょう。
cnt[x] = sum(a % x == 0 for a in A)
で定義される数列 cnt
を求めます。これは、
for a in A:
cnt[a] += 1
をした後、約数方向にゼータ変換
for p in primes:
for i in range(MX // p, 0, -1):
cnt[i] += cnt[i * p]
すれば良いです。
さて問題に戻ると、
したがって、
が答えになります。これを求めるためには、
for i in range(1, MX + 1):
if cnt[i] >= K:
ans[i] = i
をした後、倍数方向にゼータ変換(今度は累積和ではなく、累積 max)をすれば良いです。
for p in primes:
for i in range(1, MX // p + 1):
if ans[i * p] < ans[i]:
ans[i * p] = ans[i]
計算量は、
https://atcoder.jp/contests/abc393/submissions/62825909
import sys
input = sys.stdin.readline
N, K = map(int, input().split())
A = list(map(int, input().split()))
MX = max(A)
# 線形篩
primes = []
factor = [1] * (MX + 1)
for i in range(2, MX + 1):
if factor[i] == 1:
primes.append(i)
factor[i] = i
for p in primes:
if i * p > MX or p > factor[i]:
break
factor[i * p] = p
# cnt[x] = sum(a % x == 0 for a in A)
cnt = [0] * (MX + 1)
for a in A:
cnt[a] += 1
for p in primes:
for i in range(MX // p, 0, -1):
cnt[i] += cnt[i * p]
# ans[x] = max(d for d in divisor(x) if cnt[d] >= K)
ans = [0] * (MX + 1)
for i in range(1, MX + 1):
if cnt[i] >= K:
ans[i] = i
for p in primes:
for i in range(1, MX // p + 1):
if ans[i * p] < ans[i]:
ans[i * p] = ans[i]
for a in A:
print(ans[a])