• 你有一個容器,一開始是空的
• 題目有三種操作,操作總數量是 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
• 可能要把上面教的兩個算法合併在一起 (???