今天主要學習了線段樹,樹狀陣列,st表,差分,分塊和樹剖(好吧,這個已經沒聽懂了)
1.線段樹
線段樹涉及許多應用和思想,以下是今天所學
線段樹主要用於處理一段連續區間的插入,查詢,統計,查詢等操作。
複雜度:設區間長度是n,所有操作的複雜度是logn級別。
性質:線段樹是平衡的2叉樹,最大深度logn(n為線段樹所表示區間的長度)
樹中的每乙個節點代表對應乙個區間(葉子節點是乙個點……)
每個節點(所代表的區間)完全包含它的所有子孫節點 對於任意兩個節點(所代表的區間):要麼完全包含,要麼互不相交。
1)單點修改,區間查詢
先來道裸題
有個長度為n的序列a,m(m<=100000)次詢問, ·
有兩種操作: ·
1.詢問: 詢問區間l到r的數的和 ·
2.修改: 將序列中下標為p的的元素的值加v ·
要求輸出每一次詢問的結果…… (n<=100000,m<=100000)
直接上模板**(解釋見註解)
struct segtree[2*n];
void build(int p,int l,int r) //建樹,從頭結點(1,l,r)開始
void add(int p,int x,int y) //單點修改,將x出的點加y,可將讀入視為修改,從(1,i,y)開始
int mid=(tree[p].l+tree[p].r)>>1;
if(x<=mid) add(p<<1,x,y);
else add(p<<1|1,x,y);
tree[p].sum=tree[p<<1].sum+tree[p<<1|1].sum;
}int ask(int p,int l,int r) //詢問區間(l,r)元素的和
2)區間修改,區間求和,lazy思想
老樣子,來道裸題
有個長度為n的序列a,m次詢問
請實現兩種操作:
1. 詢問[l,r]的區間和
2.修改:將[l,r]的元素全部加x n<=100000,m<=100000
先來介紹lazy思想:
記錄每乙個線段樹節點的變化值,當這部分線段的一致性被破壞我們就將這個變化值傳遞給子區間,大大增加了線段樹的效率。
在此通俗的解釋lazy的意思,比如現在需要對[a,b]區間值進行加c操作,那麼就從根節點[1,n]開始呼叫update函式進行操作,如果剛好執行到乙個子節點,它的節點標記為rt, 這時我們可以一步更新此時rt節點的sum[rt]的值,sum[rt] += c* (r - l + 1),更這個節點的lazy標記。注意關鍵的時刻來了,如果此時按照常規的線段樹的update操作,這時候還應該更新rt子節點的sum值,而lazy思想恰恰是暫時不更新rt子節點的sum值,到此就return,直到下次需要用到rt子節點的值的時候才去更新,這樣避免許多可能無用的操作,從而節省時間。
#define l o<<1
#define r o<<1|1
#define ll long long
#define max 100000
struct node
tree[max<<2];
void push_up(int o)
void build(int o,int l,int r)
int mid = (l + r) >> 1;
build(l,l,mid);
build(r,mid+1,r);
push_up(o);
}void push_down(int o)
}void add(int o,int l,int r,int lazy)
push_down(o);
int mid = (tree[o].l + tree[o].r) >> 1;
if (mid >= r)
add(l,l,r,lazy);
else if (l > mid)
add(r,l,r,lazy);
else
push_up(o);
}ll ask(int o,int l,int r)
}
對於其他的單點/區間修改,區間最值其實主要對push_down和其他小細節修改即可
3)合併思想
來道裸題
單點修改,查詢區間最大連續子段和 (n<=100000,m<=100000)
分析:定義lm為區間的左連續區間的最大和,rm為區間的右連續區間的最大和
mx為區間的最大子段和,sum為區間和
對於乙個區間[l,r] mx[l,r]=max(max(mx[l,mid],mx[mid+1]),rm[l,mid]+lm[mid+1,r]])
那麼只需維護這三個量即可。
lm[l,r]=max(lm[l,mid],sum[l,mid]+lm[mid+1,r])
rm[l,r]=max(rm[mid+1,r],rm[l,mid]+sum[mid+1,r])
單點修改很容易解決了 查詢時,需要返回三元組(mx,lm,rm),即可維護答案。
2.樹狀陣列
主要解決動態區間,字首和的問題,即可以實現單點修改,詢問字首和
利用樹狀陣列+差分,也可以解決單點修改詢問區間和,區間加法詢問單點值,甚至區間加區間求和也可以做。詳細應用見傳送門
常數小,**短,二維也很容易實現
還有一些其他問題也可以通過樹狀陣列來解決,比如區間。所以一般用線段樹就夠了,樹狀陣列能做的,線段樹一定能做,很少有出題人卡這個(所以還是有的),但是又短又小又快。。。能用為啥不用
樹狀陣列可以很容易的解決最長上公升子串行,還有一些高維偏序問題一般會套用樹狀陣列,應用還有很多。
int bit[1000],n;
void add(int x,int val) //a[i]+=val 單點修改
int ask(int x) //sum[1~x]
3.st表
主要解決靜態區間,最值問題,即沒有修改,只有詢問區間最值
容易推廣到二維
倍增的思想很重要!
線段樹預處理是o(nlogn),單次查詢o(logn),空間o(n)
st表預處理是o(nlogn),單次查詢o(1),空間o(nlogn)
int log[maxn],f[17][maxn];
int ask(int x,int y)
5.後續知識點(現在先了解吧)
線段樹相關:可持久化線段樹,線段樹套bulabula,和其他各種東西結合,zkw線段樹
其他常用資料結構:trie,樹剖,splay,hash表,左偏樹,莫隊,lct,k-d tree,treap,可持久化***...
c++的pb_ds庫:封裝了很多資料結構,比如:平衡樹,雜湊表,字典樹,堆...
ACM暑期集訓2
今天主要學習了線性dp和揹包問題以及快速冪。1.整數快速冪 這個直接粘上 int qpow int x,int n res res res n n 1 return ans 2.矩陣快速冪 主要就是將整數快速冪的乘法運算換做矩陣的乘法 下面的 是方陣的快速冪 const int n 10 int t...
ACM暑期集訓5
今天主要學習力圖論基礎和最短路徑 1.圖論基礎 1 鄰接矩陣存圖 w i j 表示以ij為頂點的邊的權值 const int n 105,inf 9999999 int dis n w n n vis n n,m 鄰接矩陣存圖 for int i 1 i n i for int i 0 i2 鄰接表...
ACM暑期集訓12
今天學了字尾陣列,感覺好難理解,只能搬ppt,粘模板了 1 字尾 suffix i 為從下標i開始的字尾 string abcdef suffix 1 bcdef suffix 2 cdef 什麼是字尾陣列?把乙個字串的所有字尾按字典序進行排序。字尾陣列sa i 表示排名為i的字尾下標是什麼,rk ...