🔗 CF2179C Blackslex and Number Theory

rating: 1025

Problem Statement

題目簡述

給定一個陣列 a1,a2,,ana_1, a_2, \ldots, a_n,每次操作可以選擇索引 ii 和整數 xkx \ge k,將 aia_i 更新為 aimodxa_i \bmod x
注意每次操作時可以選擇不同的 xx
目標是通過有限次操作使數組中所有元素相等。求最大的正整數 kk,使得該目標可能達成。

Constraints

約束條件

  • 2n21052 \le n \le 2 \cdot 10^5
  • 1ai1091 \le a_i \le 10^9,且所有 aia_i 互不相同

思路:貪心策略與數論分析

這道題的核心在於觀察我們最終能將所有數字變成什麼值。由於操作是取模,數值只會變小或不變。我們主要有兩種貪心策略:

策略 1:將所有數變為 0

我們可以選擇 x=aix = a_i 來將每個 aia_i 變為 0。
這要求 aika_i \ge k 對所有 ii 成立,意味著 kmin(a)k \le \min(a)
此策略下,最大的 kk 為陣列中的最小值,且此策略總是成立。

策略 2:將所有數變為最小值 min(a)\min(a)

我們需要針對每個 aia_i 找到滿足 aimodx=min(a)a_i \bmod x = \min(a)xx。根據 aia_i 是否為最小值,分為兩種情況討論:

  • ai=min(a)a_i = \min(a)
    無需進行任何操作(或操作次數為 0),此時對 xx(以及 kk)沒有限制。
  • ai>min(a)a_i > \min(a)
    必須滿足 aimodx=min(a)a_i \bmod x = \min(a)x>min(a)x > \min(a)
    這表示 xx 必須是 aimin(a)a_i - \min(a) 的因數。
    為了讓 kk 盡可能大,我們取每個元素能容許的最大 xx,即 xi=aimin(a)x_i = a_i - \min(a)
    因此,對於所有 ai>min(a)a_i > \min(a),必須滿足 kaimin(a)k \le a_i - \min(a)

綜合上述,最嚴格的限制取決於所有 ai>min(a)a_i > \min(a)aimin(a)a_i - \min(a) 的最小值。
這發生在 aia_i 為陣列中的次小值 (asecond_mina_{second\_min}) 時,即 kasecond_minmin(a)k \le a_{second\_min} - \min(a)

Note

如果 asecond_minmin(a)min(a)a_{second\_min} - \min(a) \le \min(a),此時這個策略計算出的 kk 值會在與策略 1 (min(a)\min(a)) 的比較中被取代,故實作時不用特別檢查 x>min(a)x > \min(a) 這個條件。

結論

綜合上述兩種情況,答案即為 max(min(a),second_min(a)min(a))\max(\min(a), \text{second\_min}(a) - \min(a))

複雜度分析

  • 時間複雜度:O(n)\mathcal{O}(n)O(nlogn)\mathcal{O}(n \log n),找出最小和次小值即可。
  • 空間複雜度:O(n)\mathcal{O}(n) 用於存儲輸入,可優化至 O(1)\mathcal{O}(1)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from heapq import nsmallest

def solve():
n = int(input())
A = list(map(int, input().split()))
assert len(A) == n

# 找出陣列中最小的兩個元素
# nsmallest 可以在 O(n) 時間內找到最小的 k 個元素
a, b = nsmallest(2, A)

# 策略 1: 目標變為 0,此時 max_k = a
# 策略 2: 目標變為 a,此時 max_k = b - a (需滿足 b - a > a,否則無效,但會直接被 max 覆蓋)
print(max(a, b - a))

if __name__ == "__main__":
t = int(input())
for _ in range(t):
solve()

寫在最後

PROMPT

這裡什麼都沒有。