數列GCD
數列 GCD
數列的 GCD 具有一些很迷人的性質, 值得我用一個完整的頁面闡述.
一般地, GCD 具有可並性: 設有互不相交的多重集合 S, T , 則 $gcd(S \cup T) = gcd(gcd(S), gcd(T))$ .
如果我們給定一個數列 $A$ , 要求某個區間的 gcd , 那麽就可以使用線段樹, ST 表等區間合並的結構, 在 $O(\log n)$ 或 $O(\log ^ 2 n)$ 求解.
數列的 GCD 還具備更多的性質.
為了方便闡述, 設 $S_{l, r} = gcd(a_l, a_{l+1}, ..., a_r)$ .
當 $l$ 一定時, 可以將 $S_{l, r}$ 看作一個 $r$ 的函數, 它具有怎麽樣的性質?
1. 隨著 $r$ 的增大, $S_{l, r+1}$ 單調不上升, 前一個數一定是後一個數的倍數.
2. $S_{l, r}$ 的取值個數的上限為 $O(\log n)$ , 因為若 $S_{l, r} \ne S_{l, r+1}$ , 那麽 $S_{l, r} \ge 2 S_{l, r+1}$ , 所以序列的所有區間的不同 GCD 個數為 $O(n \log n)$ .
如何對所有 $l$ , 求出所有 GCD 不同的 r 的集合 $R_l$ , 即對於 $r \in R_l$ , $S_{l, r} \ne S_{l, r-1}$ .
我們考慮利用連續性高效處理, 得到 $O(\log ^ 2 n)$ 的做法.
考慮由 $R_{l+1}$ , 求出 $R_l$ . 由 GCD 的可並性可證明若 $S_{l+1, p} = S_{l+1, q}$ , 那麽 $S_{l, p} = S_{l, q}$ . 所以 $R_{l}$ 一定是 $R_{l+1}$ 的子集並上 $\left\{ l \right\}$ . 我們直接將 $R_{l+1}$ 中的所有元素對應的 GCD 進行更新和去重, 即可得到 $R_l$ .
實現如下:
1 static pair<int, int> tmp[S]; 2 int tot = 0; 3 4 tmp[++tot] = make_pair(pos, w);5 F(i, 1, Len) { 6 int g = gcd(w, Lis[i].second); 7 if (g != tmp[tot].second) 8 tmp[++tot] = make_pair(Lis[i].first, g); 9 } 10 11 memcpy(Lis, tmp, sizeof tmp); 12 Len = tot;
[HDU 5869] Different GCD Subarray Query
題意
給定序列 $A$ , 多次詢問某個區間的所有子區間的不同 GCD 個數.
分析
離線處理.
維護 $R_l$ 的同時, 把 $R_l$ 產生的 GCD 對每個右端點進行貢獻.
如果之前已經貢獻過當前這種 GCD , 那麽先清楚掉之前的貢獻.
1 F(i, 1, Len) { 2 if (Last[Lis[i].second] > 0) 3 add(Last[Lis[i].second], -1); 4 add(Last[Lis[i].second] = Lis[i].first, +1); 5 }
數列GCD