• 沒有找到結果。

根號算法

N/A
N/A
Protected

Academic year: 2022

Share "根號算法"

Copied!
60
0
0

加載中.... (立即查看全文)

全文

(1)

根號算法

credit by nkhg (t1016d) modified by yp155136

2021/06/05

(2)

課程大綱

• 暖身:折半枚舉

• RMQ(Range Minimum Query)

• 區間加值、區間求和

• 根號算法應用(一)

• 根號算法應用(二):Counting Triangles

(3)

暖身:芽芽的撲克牌

• 芽芽有 張撲克牌,每張撲克牌都有它的美好度

• 請你從這 張撲克牌挑一些,使得美好度的總和恰好為 M

• M、每個 、 的總和都在

(4)

芽芽的撲克牌

• 我會做

• 大約是

• 好像有點大

• QQ (?)

• 折半枚舉

(5)

芽芽的撲克牌

• 如果我們可以把問題分割成「規模相近」的兩部分

• 而且可以用「好方法」把問題的兩個部分合併

• 那就有機會藉此減少時間複雜度!(類似分治)

• 40個物品…任意分割成兩個大小類似的集合

(6)

芽芽的撲克牌

3 7 12 5 1 4

• 左半邊可以組合出什麼組合呢

• {}、{3}、{7}、{12}、{3,7}、{3,12}、{7,12}、{3,7,12}

• 總和分別為 {0,3,7,12,10,15,19,33}

• 那麼, 右半邊呢

• {}、{5}、{1}、{4}、{5,1}、{1,4}、{5,4}、{5,1,4}

• 總和分別為 {0,5,1,4,6,5,9,10}

(7)

芽芽的撲克牌

• 如果我們想要湊出總和為 M

• 左半邊總和分別為 {0,3,7,12,10,15,19,33}

• 右半邊總和分別為 {0,5,1,4,6,5,9,10}

• 如果左邊所取的物品,價值總和為 i

• 右邊就必須拿到 M-i

(8)

芽芽的撲克牌

• 分別考慮左邊的所有物品能湊出的價值

• 分別考慮右邊的所有物品能湊出的價值

• 需要多少時間? 分別需要

• 我們總共想要價值M

• 枚舉左邊集合的所有價值 i

• 如果在右邊找的到 M-i,那就成功了

• 如果右邊找不到M-i呢? 那就表示不存在「從左邊集合取出總和 i, 右邊集合取出總和 M-i 的方法」

(9)

芽芽的撲克牌

• 如何從右邊的集合中尋找 M-i 呢?

• 先排序! 再搜尋

• 每一個

• ->

• 雖然不太爽,但至少比原先的 快多了

(10)

芽芽的撲克牌

• 要如何找出一組可行的方案?

• Struct Poker {

• long long sum; //儲存總和

• bool used[N]; //紀錄每個元素是否用到

• }

(11)

芽芽的撲克牌

• Struct Poker { // 照sum排序

• long long sum; // 儲存總和

• int used; // 紀錄每個元素是否用到,用一個int就夠了

• } //上面其實也可以用std::pair寫

• 因為搜索的問題本身規模不會太大

• 位元運算是方便好寫的好選擇

• 很快,很節省空間

(12)

芽芽的撲克牌

• 實做範例

• 常用一個整數 mask 代表每個數字選擇 的狀態

• 例如:假設 nn = 3 如果 mask = 6 , 寫成二進制是 110 , 代表選擇 a[1] 跟 a[2]

(13)

暖身完畢

• 暖身完畢,進入正題吧~

(14)

RMQ(Range minimum query)

• 給你一個長度 N 的序列 A[0], A[1], ..., A[N - 1]

• 緊接著 Q 筆詢問,每筆詢問是一個數對 x, y

• 請你回答 min{ A[x], A[x + 1], ..., A[y] }

• A = {5, 2, 8, 1, 7}

• Query = {(0, 2), (1, 4), (2, 2)}

• 答案:2 1 8

(15)

RMQ(Range minimum query)

• 時間複雜度

• 特性:min(a, b, c) = min(a, min(b, c))

• 可以對一部分數字先求最小值,再從每個部份的最小值中取最小 的!

(16)

RMQ(Range minimum query)

• 一部分?

• 每連續 k 個分一塊,先把每塊的最小值算好並存下來

• 對於詢問

• 綠色每塊都先算好了,時間花費是塊數:最多

• 紅色部份每個數字跑一遍,時間花費是一塊大小:最多 2k

• 下面的說明中,綠色會是「大塊」,紅色會是「小塊」

(17)

RMQ(Range minimum query)

• 每次詢問

取個好的 k?

• 算幾不等式:

• 在 也就是 時最小

• 所以取 k 為 就有了每次詢問 的解法了

• 預處理 O(N)

(18)

RMQ(Range minimum query)

• 於是大家獲得了人生第一個根號算法 (?)

• RMQ 是很經典的問題

• 線段樹:預處理 O(N) - 詢問 O(log N)

sparse table:預處理 O(N log N) - 詢問 O(1)

sparse table 套 sparse table:預處理 O(N log log N) - 詢問 O(1)

RMQ 轉成 LCA 問題

• 線性 RMQ:預處理 O(N) - 詢問 O(1)

• 資芽只會教到線段樹

(19)

實作上

(20)

實做技巧

(21)

實做技巧

(22)

區間加值、區間求和

• 給你一個長度為 N 的序列 a[0], a[1], ..., a[N - 1]

• 現在有兩種操作:

• 區間加值:給定 L, R, x ,

請把 a[L], a[L + 1], ..., a[R] 加上 x

• 區間求和:給定 L, R ,

請求出 a[L] + a[L + 1] + ... + a[R]

• A = {3, -5, 4, 2}

區間加值 (1, 2, 10) 後,A = {3, 5, 14, 2}

區間詢問 (0, 2) ,得到總和 3 + 5 + 14 = 22

(23)

單點加值、區間求和

• 區間加值太難了,先試試看單點加值吧

• 給你一個長度為 N 的序列 a[0], a[1], ..., a[N - 1]

• 現在有兩種操作:

• 單點加值:給定 pos, x , 請把 a[pos] 加上 x

• 區間求和:給定 L, R ,

請求出 a[L] + a[L + 1] + ... + a[R]

(24)

單點加值、區間求和

• 最簡單的想法:

• 單點加值 O(1)

• 區間求和 O(N)

• TLE

(25)

單點加值、區間求和

• 區間求和太慢了

• 試試看跟 RMQ 一樣,把連續 k 個東西分成一塊

• 每塊維護裡面元素的總和

• 詢問 a[x] 到 a[y] 的總和,也可以跟 RMQ 一樣,分成大塊

(綠色部份)以及小塊(紅色部份)。把這些部份的總和加起來,就 是答案。

(26)

單點加值、區間求和

• 把連續 k 個東西分成一塊,每塊維護裡面元素的總和

• 對於單點加值,除了把那個點的值更新之外,還需要把那個點所 位於的塊的總和也更新。

• 區間求和 ,單點改值 O(1)

• 取 ,可以得到 查詢,O(1) 修改的複雜度

(27)

單點加值、區間求和

• 實做範例

• 跟前面 RMQ 一樣,

常用 i / K 代表 a[i] 所在的塊

(28)

單點加值、區間求和

• 實做範例

(29)

區間加值、區間求和

• 會單點加值後,來試試看區間加值吧~

• 可不可以把區間加值看成很多單點加值呢?

• 不行

• 區間加值複雜度最慘是 O(n) * O(1) = O(n)

• 還是太慢了 QQ

• 有沒有辦法改進呢?

(30)

區間加值、區間求和

• 把連續 k 個東西分成一塊,每塊維護裡面元素的總和

• 如果把區間加值的左界、右界,也看成上面那樣(小塊 + 大塊 + 小塊),會發生什麼事情呢?

• 可以發現,除了左右兩個小塊之外,「對於大塊來說,就是全部 的元素通通加上 x」。

• 既然是全部的元素通通加上一個值,我們可不可以從中得到一 些啟發呢?

(31)

區間加值、區間求和

• 把連續 k 個東西分成一塊,每塊維護裡面元素的總和

• 除了左右兩個小塊之外,「對於大塊來說,就是全部的元素通通 加上 x」。

• 因此,對於每個大塊,除了開一個變數 sum[i] 維護這個塊的 總和之外,還可以額外開一個變數 tag[i] ,維護這個塊「全部 的維護一起被加上多少」。

• 有了 tag 後,區間加值加到大塊的部份,直接寫 tag[i] +=

x; 就行了。

(32)

區間加值、區間求和

• 把連續 k 個東西分成一塊,每塊維護裡面元素的總和

• 對於每個大塊,除了開一個變數 sum[i] 維護這個塊的總和之 外,還可以額外開一個變數 tag[i] ,維護這個塊「全部的維護 一起被加上多少」。

• 有了 tag 後,區間加值加到大塊的部份,直接寫 tag[i] +=

x; 就行了。

• 複雜度分析:區間加值 ,區間求和 ,整體 的複雜度取 ,可以得到 ,是一個可以接受的 複雜度!

(33)

區間加值、區間求和

• 實做細節頗多,這邊留給大家回去練習

(34)

有趣的應用題

• 給你 n 個正整數 a[1], a[2], ..., a[n] ,並且保證 a[1] + a[2] + ... + a[n] = K 。接著,有 Q 筆詢問,

每筆詢問會給你一個數字 x ,請你回答,x 是不是可以寫成那 n 個數字中的某些數字的總和。

• 假設 a = {3, 6, 2, 2}

• 7 可以寫成 3 + 2 + 2 ,11 可以寫成 3 + 6 + 2 ,但是 12 就沒有辦法寫成 a 的某些數字的總和。

• 先備知識:有限背包問題

(35)

有趣的應用題

• 好像可以 dp

• dp[x] = {true / false},代表 x 可不可以被組出來(寫成 那 n 個數字的總和)

• 或者是可以想像成有 n 個物品,第 i 個物品的重量、價值都 是 a[i] 。最後如果 dp[x] == x ,就代表 x 可以被組出 來。

• 複雜度?

• worst case O(n) * O(K) ,似乎是個 ,吃個大大的 TLE 。

• 似乎有些性質沒有用到?

(36)

有趣的應用題

• 一個怪怪的條件:a[1] + a[2] + ... + a[n] = K

• 而且

• 或許我們可以從這方面著手(?)

• 如果我們選擇一個 x ,把 a 的數字分成兩類: 和 這兩種

• 會發生什麼有趣的事情呢?

• 的數字中,最多只會有 個。

(37)

有趣的應用題

• 一個怪怪的條件:a[1] + a[2] + ... + a[n] = K

• 而且

• 的數字中,最多只會有 個。

• 因此,如果對 的數字做「有限背包問題」(統計每個數字出 現過得個數), 的數字做正常的背包問題

• 我們可以得到

• 取 ,就可以得到 的複雜度

• 是一個可以接受的複雜度!

(38)

有趣的應用題

• 從上面的題目中,我們可以進一步發現一個性質:

• 如果有 a[1] + a[2] + ... + a[n] = K 這個條件,那 麼「a 陣列最多只會有 種不同的數字」。

• 有的時候,這個性質往往可以帶來很好的事情

• 根號算法不一定是單純的把序列每 個切成一塊,也可以 是設定某個臨界值,小於臨界值做某一些事情,大於臨界值做另 外一種事情。

• 臨界值的選擇常常是根號。

• 根號算法可以用的地方比想像中多很多,也常常配合一些不同 的演算法使用,敬請期待(?)

(39)

在線與離線

對於剛剛很多題這種有多個詢問、操作的題目

• 在線 (online):程式/演算法 必須對前一個詢問或操作做出 回答,之後才能知道下一個詢問或操作。

(換句話說就是寫程式讀了一行就要馬上處理它)

(不用懷疑,一定有辦法作到這件事情XD)

• 離線 (offline):程式/演算法 不須先對前一個詢問或操作 做出回答,就能知道下一個詢問或操作

(換句話說你的程式可以先把全部輸入讀進來再來想辦法)

(40)

在線與離線

• 為什麼要區分在線與離線呢?

• 哪一個比較簡單?

(41)

在線與離線

• 將來有很多離線比較簡單的例子在等著大家 xdd

(42)

Counting Triangles

• 給你一個 N 個點、M 條邊的簡單無向圖,請算出這張圖裡有幾 個「三角形」?

• 假設點被編號為 0 到 N - 1,三角形定義為一組數對 (x, y, z) 使得 且

(x, y)、(y, z)、(z, x) 均有邊

(43)

Counting Triangles

• 假設我們能夠 O(1) 回答某個給定的 pair (x, y) 是否為圖上的一條邊

• 那麼指定任一個點 v,我們能夠

a. 在 O(M) 時間內算出圖中有幾個包含 v 的三角形 b. 在 時間內算出圖中有幾個包含 v 的三角形

(其中 d 為 v 的點度)

• 每個三角形包含恰 3 個點,

所以對所有點算出「包含它的三角形個數」加總除以 3,

就是答案了!

(44)

Counting Triangles

• 假設我們能夠 O(1) 回答某個給定的 pair (x, y) 是否為圖上的一條邊

• 按照點度是否大於 K 分兩條

• 大於 K 的: 作法 a. (O(M))

• 這種點不超過 2M / K 個,總共

• 不超過 K 的:作法 b. (O(d^2))

• 這種點最多 N 個,每個 d 最大到 K,總共

(45)

Counting Triangles

• 假設我們能夠 O(1) 回答某個給定的 pair (x, y) 是否為圖上的一條邊

• 按照點度是否大於 K 分兩條

• 大於 K 的: 作法 a. (O(M))

• 這種點不超過 2M / K 個,總共

• 不超過 K 的:作法 b. (O(d^2))

• 這種點最多 N 個,每個 d 最大到 K,總共 O(N * K^2) ???

(46)

Counting Triangles

• 假設我們能夠 O(1) 回答某個給定的 pair (x, y) 是否為圖上的一條邊

• 按照點度是否大於 K 分兩條

• 又是算幾砸下去,取

• 得到

• 於是這題被完美的解決了(?)

(47)

Counting Triangles

假設

我們能夠 O(1) 回答某個給定的 pair (x, y) 是否為圖上的一條邊

• 那麼指定任一個點 v,我們能夠

a. 在 O(M) 時間內算出圖中有幾個包含 v 的三角形

• 開一條 bool 陣列 adj,adj[i] 表示 v 與 i 之間是否有邊

b. 在 時間內算出圖中有幾個包含 v 的三角形

(其中 d 為 v 的點度)

• 我們只在乎這邊給出的所有 (x, y) 詢問總共有幾個為 true 而且照 a. 的作法,只要一堆連續的詢問有共同端點我們就會做了

離線!

把全部詢問照端點分類存起來最後再做

(48)

Counting Triangles

實做的部份會等到最後面的時候再來討論

(49)

Counting Triangles

• 另一個結論可能比較簡潔但思路似乎沒那麼自然的作法

(不過它或許會給大家一些啟示)

• 對於一條邊 (u, v),我們可以用 O(d[u] + d[v]) = O(max(d_u, d_v)) 算出包含這條邊的三角形個數(d_u, d_v 是 u 和 v 的點度)

• 只要一開始把每個點的相鄰點排序過,就可以好好的在 O(d_u + d_v) 內算出

• 如果 (u, v) 中點度較大的那個點,我們有它的相鄰矩陣,那麼同一件 事可以做到 O(min(d_u, d_v))

(50)

Counting Triangles

• 把點度 的點叫做「重點」,其他叫「輕點」

• 重點只有

• 所以把「重點」對所有點的相鄰矩陣建出來

• 空間也只要

• 對所有邊計算它被幾個三角形包含

• 輕點-輕點:用 O(max(d_u, d_v)) 的作法,也才

• 輕點-重點:用 O(min(d_u, d_v)) 的作法,也才

• 重點-重點:用 O(min(d_u, d_v)) 的作法,也才 ...?

(51)

Counting Triangles

• 對一個重點 u,如果計算某條邊 (u, v) 的 cost O(min(d_u, d_v)) 是 d_u

表示這條邊連接的點 v 具有 d_v > d_u

這種點 v 最多只有 個

所以以 d_u 為 cost 的邊的花費加總起來也才

• 重點只有 個,它們的總花費就是

• 三種情況都在 內,解決!

(52)

Counting Triangles

• 把點度 的點叫做「重點」,其他叫「輕點」

• 重點只有

• 所以把「重點」對所有點的相鄰矩陣建出來

• 空間也只要

• 對所有邊計算它被幾個三角形包含

• 輕點-輕點:用 O(max(d_u, d_v)) 的作法,也才

• 輕點-重點:用 O(max(d_u, d_v)) 的作法,也才 ...?

• 重點-重點:用 O(min(d_u, d_v)) 的作法,也才

(53)

Counting Triangles

(54)

Counting Triangles

• 把點度 的點叫做「重點」,其他叫「輕點」

• 重點只有

• 所以把「重點」對所有點的相鄰矩陣建出來

• 空間也只要

• 對所有邊計算它被幾個三角形包含

• 輕點-輕點:用 O(max(d_u, d_v)) 的作法,也才

• 輕點-重點:用 O(min(d_u, d_v)) 的作法,也才

• 重點-重點:用 O(max(d_u, d_v)) 的作法,也才 ...?

(55)

Counting Triangles

(56)

Counting Triangles

• 重點整理與啟發

第一種作法枚舉點、第二種作法枚舉邊

找出枚舉後的複雜度關鍵是什麼,對那個東西分大小

• 第一種作法用到的技巧

離線

可重複利用並快速歸零的 bool array

• 第二種作法用到的技巧

「重點」很少,可以維護一個「重點」X「所有點」的二維陣列

• 小心估複雜度

(57)

Counting Triangles

• 有一天,你在路上撿到一份報紙

• 報紙上寫著:「假設可以 O(1) 回答 (x, y) 是否為圖中的一個 邊。對於每條邊 (u, v) ,你花 O(min(d_u, d_v)) 去算出包 含這條邊的三角形個數。只要這樣做,你就可以 AC counting triangles 了。假設題目條件的限制跟前面一樣」

• 那份報紙的敘述真的是正確的嗎?

• 如果是的話,請試著證明看看

• 如果不是的話,請試著構造一個反例

• 鼓勵大家這時候按個暫停,想想看這樣是不是正確的

(58)

Counting Triangles

• 這個作法是好的,最終的複雜度為

• 想法說明如下:

• 不失一般性假設一條邊 (u, v) 中,d_u <= d_v

• 如果 u 是輕點,那麼最慘為 ,整體複雜度為

• 如果 u 是重點,考慮到符合 d_u <= d_v 的點最多只會有 個,因此產生出來的詢問邊數最多只有

而因為重點最多只有 個,因此最終的複雜度為

• 優點是非常的好寫(只需要處理均攤 O(1) 查詢一條邊有沒有 出現在圖中)

• 複雜度的證明相較起來,沒有那麼的直觀

(59)

Counting Triangles

• 實做細節如下:

(60)

Counting Triangles

• 實做細節如下:

參考文獻

相關文件

在選擇合 適的策略 解決 數學問題 時,能與 別人溝通 、磋商及 作出 協調(例 如在解決 幾何問題 時在演繹 法或 分析法之 間進行選 擇,以及 與小組成 員商 討統計研

• 我們通常用 nD/mD 來表示一個狀態 O(N^n) ,轉移 O(N^m) 的 dp 演算法. • 在做每題

•  問你當前的序列裡的第 k個人是誰 (區間詢問?). • 

•  問你當前的序列裡的第 k個人是誰 (區間詢問?). • 

將繩子折成相等的四小段,已知每一小段都是 x 公分,. 已知繩子的全長為

 點擊按鈕「Rollover」,工作表便會剪下紅色線以下的資料並複 製至綠色線以下的儲存格。

• 在線 (online):程式/演算法 必須對前一個詢問或操作做出

RMQ(Range minimum