• 沒有找到結果。

第 Z 大

在文檔中 根號算法 (頁 54-121)

• 你有一個容器,一開始是空的

• 題目有三種操作,操作總數量是 Q:

加值:把 y 個數字 x 丟到容器裡面

減值:把 y 個數字 x 從容器中丟掉

查詢:詢問這個容器的第 Z 大的元素

• 1 <= Q, x <= 10^5, y <= 10^9 ,Z保證合法

第 Z 大

第 Z 大

• 當然,這題可以用平衡樹、線段樹輕鬆過

• 不過老天爺還是希望你可以使用分「塊」

• 不過是什麼「塊」呢?

第 Z 大

第 Z 大

• 對題目的「值域」分塊

• 對這題來講,就是對數字 x 去分塊

• 假設把值域 K 個做一塊,並且維護好每一塊的總和

也就是維護每一塊中,每一個數字出現過得次數和

第 Z 大

• 對題目的「值域」分塊

第 Z 大

• 對題目的「值域」分塊

• 對這題來講,就是對數字 x 去分塊

• 假設把值域 K 個做一塊,並且維護好每一塊的總和

也就是維護每一塊中,每一個數字出現過得次數和

• 加值、減值操作:

O(1) 更新那個數字出現的次數和,以及相對應塊的總和

• 查詢操作:

先花 O(C/K) 的時間,找到相對應的數字在哪一塊

C代表值域,本題來講C = 2*10^5

第 Z 大

• 對題目的「值域」分塊

• 對這題來講,就是對數字 x 去分塊

• 假設把值域 K 個做一塊,並且維護好每一塊的總和

也就是維護每一塊中,每一個數字出現過得次數和

• 加值、減值操作:

第 Z 大

• 對題目的「值域」分塊

• 對這題來講,就是對數字 x 去分塊

• 假設把值域 K 個做一塊,並且維護好每一塊的總和

也就是維護每一塊中,每一個數字出現過得次數和

• 加值、減值操作:

O(1) 更新那個數字出現的次數和,以及相對應塊的總和

• 查詢操作:

先花 O(C/K) 的時間,找到相對應的數字在哪一塊

C代表值域,本題來講C = 10^5

再花 O(K) 的時間,確認那個數字是哪一個數字

• 總複雜度為O(Q(C/K + K)),取 K = sqrt(C)

小咲的玩具

• NPSC 2019 國中組初賽 pB

• 你有 K 個 vector<int> v[K], vector 裡面總共會有 N 的元素

小咲的玩具

• 不是序列題,要怎麼分塊QAQ

小咲的玩具

小咲的玩具

• 先不管這個,現在假設詢問的 vector<int> 的 size 分別是 x, y,並且 x < y。

• 有沒有什麼方法可以得到答案(?)

• 方法一: O(xy) 暴力枚舉

小咲的玩具

• 先不管這個,現在假設詢問的 vector<int> 的 size 分別是

小咲的玩具

• 先不管這個,現在假設詢問的 vector<int> 的 size 分別是 x, y,並且 x < y。

• 有沒有什麼方法可以得到答案(?)

• 方法一: O(xy) 暴力枚舉

• 方法二: O(x + y) ,使用 雙指針(two pointer)

方法二不知道的話沒關係^^

• 方法三: O(x log y)

先維護好前綴和(prefix sum),之後對於 x 的每個元素,在 y 二分搜出哪個位置比他小

小咲的玩具

• 方法三: O(x log y)

小咲的玩具

• 把那先 vector<int> 分成兩類

胖 vector : size > K

瘦 vector : size <= K

小咲的玩具

小咲的玩具

• 把那先 vector<int> 分成兩類

胖 vector : size > K

瘦 vector : size <= K

• 先預處理詢問是兩個胖 vector 的所有答案

複雜度為: O( (N/K)^2 K log N ) = O(N^2/K log N)

小咲的玩具

• 把那先 vector<int> 分成兩類

小咲的玩具

• 把那先 vector<int> 分成兩類

胖 vector : size > K

瘦 vector : size <= K

• 先預處理詢問是兩個胖 vector 的所有答案

複雜度為: O( (N/K)^2 K log N ) = O(N^2/K log N)

• 對於其他的詢問(胖&瘦 或者 瘦&瘦),就直接算答案

複雜度為: O( N K log N)

小咲的玩具

• 把那先 vector<int> 分成兩類

胖 vector : size > K

瘦 vector : size <= K

小咲的玩具

• 把那先 vector<int> 分成兩類

胖 vector : size > K

瘦 vector : size <= K

• 先預處理詢問是兩個胖 vector 的所有答案

複雜度為: O( (N/K)^2 K log N ) = O(N^2/K log N)

• 對於其他的詢問(胖&瘦 或者 瘦&瘦),就直接算答案

複雜度為: O( N K log N)

• 總複雜度為: O(N^2/K log N + NK log N)

• 取 K = sqrt(N) ,可得複雜度為 O(N^1.5 log N)

在線與離線

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

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

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

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

在線與離線

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

• 哪一個比較簡單?

在線與離線

在線與離線

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

在線與離線

• 其實不用等到將來,剛剛就有一個小小的例子(?)

Counting Triangles

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

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

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

• 3 <= N <= 10^5

• 0 <= M <= 10^5

Counting Triangles

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

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

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

Counting Triangles

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

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

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

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

Counting Triangles

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

Counting Triangles

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

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

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

• 這種點不超過 2M / K 個,總共 O(M^2 / K)

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

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

Counting Triangles

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

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

Counting Triangles

假設

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

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

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

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

(其中 d 為 v 的點度)

Counting Triangles

假設

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

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

Counting Triangles

假設

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

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

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

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

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

(其中 d 為 v 的點度)

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

Counting Triangles

假設

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

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

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

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

Counting Triangles

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

Counting Triangles

Counting Triangles

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

Counting Triangles

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

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

• 對於一條邊 (u, v),我們可以用 O(max(d_u, d_v)) 算出包含這條

Counting Triangles

• 把點度 >= sqrt(M) 的點叫做「重點」,其他叫「輕點」

• 重點只有 2*sqrt(M) 個

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

• 空間也只要 O(N sqrt(M))

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

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

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

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

Counting Triangles

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

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

這種點 v 最多只有 2M / d_u 個

Counting Triangles

• 把點度 >= sqrt(M) 的點叫做「重點」,其他叫「輕點」

• 重點只有 2*sqrt(M) 個

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

• 空間也只要 O(N sqrt(M))

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

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

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

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

Counting Triangles

Counting Triangles

• 把點度 >= sqrt(M) 的點叫做「重點」,其他叫「輕點」

• 重點只有 2*sqrt(M) 個

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

• 空間也只要 O(N sqrt(M))

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

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

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

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

Counting Triangles

Counting Triangles

• 重點整理與啟發

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

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

• 第一種作法用到的技巧

離線

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

• 第二種作法用到的技巧

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

• 小心估複雜度

Mo’s algorithm

https://zerojudge.tw/ShowProblem?problemid=b417

• 先來看一道經典問題:「區間 種樹(X) 眾數(O)」

Mo’s algorithm

• 發明者:中國隊隊長莫濤

• 莫濤在解決「小Z的襪子」這題是,想到的演算法

https://www.lydsy.com/JudgeOnline/problem.php?id=

2038

• 於是乎,這個算法就被稱為「莫隊算法」,簡稱「莫隊」、

「Mo's algo」

Mo’s algorithm

• 莫隊算法(Mo's algorithm)是一個離線的算法

• 先把詢問「按照某種順序」排序過後,開始暴力硬算

• 題目的詢問從 [L, R] 轉移到 [L, R+1 or R-1] 時,必須

Mo’s algorithm

• 假設上面那張投影片的所有轉移時間都是 O(1)

• 那在「經過某種順序排序」後的詢問之下,時間複雜度就是

O(L指針移動的次數) + O(R指針移動的次數) + O(Q * 得到 答案的複雜度)

Mo’s algorithm

• 假設上面那張投影片的所有轉移時間都是 O(1)

Mo’s algorithm

• 先把序列每 K 個分成一塊。

Mo’s algorithm

Mo’s algorithm

• 先把序列每 K 個分成一塊。

• 排序詢問的方法為:

先按照 L 所在的塊排序

如果 L 所在的塊相同,則按照 R 排序

• 認真分析一下複雜度:

對於 L 指針:

對於 R 指針:

Mo’s algorithm

• 先把序列每 K 個分成一塊。

• 排序詢問的方法為:

先按照 L 所在的塊排序

如果 L 所在的塊相同,則按照 R 排序

Mo’s algorithm

• 先把序列每 K 個分成一塊。

• 排序詢問的方法為:

先按照 L 所在的塊排序

如果 L 所在的塊相同,則按照 R 排序

• 認真分析一下複雜度:

對於 L 指針:

O(N * K + N)

對於 R 指針:

O(N/K * N)

• 取 K = sqrt(N) ,大勝利~

• 於是乎,大家就得到一個 O(N sqrt (N))的區間眾數的解法

Mo’s algorithm

Mo’s algorithm

• 參考實做方式:指針的移動方式

Mo’s algorithm

Mo’s algorithm

• 進階版:帶修改莫隊

• 複雜度是 O(N^(5/3))

• 就留給大家上網查資料囉

Mo’s algorithm

題目

https://codeforces.com/contest/785/problem/E

• 給你一個長度為 N 的序列,一開始是 A[1] = 1, A[2] = 2, ..., a[N] = N

• Q 筆操作,每筆操作會給 X, Y ,請執行 swap(A[X],A[Y])

• 每筆操作完後,請輸出當前有多少對「逆序數對」

• N <= 2 * 10^5, Q <= 5 * 10^4

• TL = 4sec, ML = 512MB

• 當然這題有很多其他的方法、資料結構可以揍掉這題

題目

https://oj.icpc.tw/c/28/D

• 給你一個 N 個點、M 條邊的簡單無向圖

題目

區間第 K 大

• 給你一個長度為 N 的序列,並且有 Q 筆詢問

• 每筆詢問的內容是:[L, R]之中,第 K 大的數字是多少

• N, Q, 值域 <= 10^5

• 可能要把上面教的兩個算法合併在一起 (???

在文檔中 根號算法 (頁 54-121)

相關文件