AcWing算法提高课 Level-3 第四章 高级数据结构
生活随笔
收集整理的這篇文章主要介紹了
AcWing算法提高课 Level-3 第四章 高级数据结构
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
并查集
1250. 格子游戲
- 并查集解決的是連通性(無向圖聯通分量)和傳遞性(家譜關系)問題,并且可以動態的維護。拋開格子不看,任意一個圖中,增加一條邊形成環當且僅當這條邊連接的兩點已經聯通,于是可以將點分為若干個集合,每個集合對應圖中的一個連通塊。
- 并查集(典型并查集判斷是否存在環的問題)
- 并查集中一般用一維的坐標會方便一些,所以這個題要把二維坐標轉化成一維坐標,有個很常用的方式,(x,y) -> x*n+y,前提是x和y都從0開始
- 將每個坐標看成一個點值,為了方便計算,將所有的坐標橫縱坐標都減1,第一個位置即(1,1)看成是0,(1,2)看成是1,依次類推,將所有的坐標橫縱坐標都減1后,假設當前點是(x,y),則該點的映射值是a = (x * n + y),若向下畫,則b = [(x + 1) * n + y],若向右畫,則b = [x * n + y - 1]
- 并查集操作復雜度接近O(1),本來應該這題是O(n+m),但由于實現時只用了路徑壓縮,沒有用什么什么合并,所以本質這道題復雜度是mlogn的,但這個logn非常小,因為并查集常數很小,
樹狀數組
- 樹狀數組相較于數組和前綴和數組的作用
- 采取的是二進制思想,例如求1~x的和,就可以用下圖的方案在log(x)中算出;即可以在log(n)時間內算出前n項的前綴和
- 那如何求每個區間的和呢?先看看區間的性質,第一個區間包含2i12^{i_1}2i1?個數,第二個區間包含2i22^{i_2}2i2?個數,以此類推,然后發現2i12^{i_1}2i1?是x的二進制表示的最后一位1,2i22^{i_2}2i2?是x - 2i12^{i_1}2i1?的最后一位1。因此,在這樣的(L, R]區間中,這個區間長度一定是R的二進制表示的最后一位1所對的次冪。(lowbit(x)是O(1)的),那么這個區間形式就可以改寫為[R-lowbit( R)+1,R],那么就可以用數組,C[R]來表示這個區間的總和了,這樣的區間最多有n個。
- C[x] = a[x - lowbit( x) + 1, x],對于每個x而言,1~x的所有數的和,最多可以拆成log(x)個C[x]的和,所以是log(n)
- 這樣就挖掘出了所有不同C之間的關系
241. 樓蘭圖騰
#include <iostream> #include <cstring>using namespace std;typedef long long ll;const int N = 2e5 + 10;int n; int a[N], tr[N]; int Greater[N], lower[N];int lowbit(int x) {return x & (-x); }void add(int x, int c) {for (int i = x; i <= n; i += lowbit(i)) tr[i] += c; }int sum(int x) {int res = 0;for (int i = x; i; i -= lowbit(i)) res += tr[i];return res; }int main() {scanf("%d", &n);for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);// 從左到右for (int i = 1; i <= n; i ++ ){int y = a[i];Greater[i] = sum(n) - sum(y); // [y + 1, n] // y + 1 ~ n之間已出現的數的個數lower[i] = sum(y - 1); // [1, y - 1] // 1 ~ y - 1之間已出現的數的個數add(y, 1); // 記錄y這個數已經出現了}memset(tr, 0, sizeof tr); // 清空,重新建樹,之間是從左向右遍歷數組建樹,后面要從右向左了ll res1 = 0, res2 = 0;for (int i = n; i; i -- ){int y = a[i];res1 += Greater[i] * (ll)(sum(n) - sum(y));res2 += lower[i] * (ll)(sum(y - 1));add(y, 1);}printf("%lld %lld", res1, res2);return 0; }線段樹
// 簡單線段樹的操作 ://傳入節點編號,用子節點信息來算父節點信息 push up(int u)//將一段區間初始化為一顆線段樹 build()//修改操作,修改某一個點或者某一個區間(懶標記) modify()//查詢某一段區間的信息 query()- 定義 :線段樹是一顆滿二叉樹,以一顆長度為10的序列為例 :
- N個葉結點的滿二叉樹有N + N/2 + N/4 + … = 2N-1個結點,因為在上述存儲方式下,最后還有一層產生了空余,最后一層最壞情況下是上一層的兩倍,也就是還有2N個,所以保存線段樹的數組長度要不小于4N
- 線段樹的查詢操作,例如查詢【5, 9】,有8個區間要查 。且可以發現為log(n),不過最多有4logn,而樹狀數組只有1logn,所以線段樹復雜度更大
- 線段樹的結構 :
- 線段樹的建樹 :
- push_up操作 :
- 查詢操作 :
- 修改操作
1275. 最大數
- 這道題的兩個操作可以被轉化成上述兩個操作,而這兩個操作就是線段樹的經典操作,動態修改某個位置上的數,動態查詢某個區間內的最大值
- 不過,這個問題比較特殊,每個位置只會被修改一次,因此這題可以用RMQ算法做,可以看作靜態問題,不過局限性比較大,只能處理靜態數據。但其實不是如此,這題由于每次修改的時候依賴上一次查詢的,是動態的問題,不能把它全部讀進來然后預處理去做的,所以不能用RMQ
- 注意!!凡是只要修改某一個點,單點,的,都不需要用到懶標記,本身都可以做到Logn的復雜度;凡是涉及到區間的,修改整一個區間的,這樣的操作,一般都需要加上懶標記,否則復雜度會退化
- pushup由兒子算父節點的信息,pushdown是父節點的修改更新到兒子上面
可持久化數據結構
- 可持久化的前提 :這個數據結構本身的拓撲結構在操作過程中保持不變。
- 可持久化數據結構 :1.trie的可持久化。2.線段樹的可持久化-主席樹。比如線段樹,只會變線段樹里面的信息,線段樹本身是不會發生變化的。樹狀數組每個結點的兒子也是固定不變的,變的只是里面存的信息。堆也是,結構不會發生變化,完全二叉樹;而有些數據結構會發生拓撲序的變化,比如平衡樹,左旋和右旋,操作后結點之間的拓撲序會發生變化。
- 可持久化解決的問題是什么 :可以存下來數據結構的所有歷史版本
- 核心思想 :只會記錄當前版本和前一個版本不一樣的地方。比如線段樹,每次修改,如果n個結點,操作涉及最多4logn個結點,每次操作最多改變的節點數只有o(logn)個。即,最多只會記錄mlogn個結點
- 可持久化trie :
- 凡是有變化的點就裂開成一個新的點,比如根結點就每次必然有變化
256. 最大異或和
- 原始數據最多有3e5,再加上3e5個操作,因此整個序列長度最大是6e5
- 2^24正好大于10 ^7,所以trie長度最多為24,再加上根結點,所以每次最多建立25個新結點
平衡樹Treap
253. 普通平衡樹
AC自動機
1282. 搜索關鍵詞
總結
以上是生活随笔為你收集整理的AcWing算法提高课 Level-3 第四章 高级数据结构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 黑马程序员pink老师前端入门教程,零基
- 下一篇: 树状数组 求 逆序对