22703 王培軒
假設有陣列\(a\),定義前綴和(prefix sum)陣列\(b\)
\(b_i=\sum_{k=1}^{i}a_k\)
\(a\):1 2 3 4 5
prefix sum:1 3 6 10 15
\(O(n)\)預處理
\(O(1)\)求任意\([l,r]\)區間和
假設有陣列\(a\),定義差分(differences)陣列
\(d_i=a_i-a_{i-1}\),且\(d_0=a_0\)
即可得\(a_i=\sum_{k=1}^{i}d_i\)
前綴的逆運算為差分
\(a\):1 3 6 10 15
differences:1 2 3 4 5
暫時沒有想到裸的運用><
但前綴跟差分到處都可以派上用場
(Segment Tree)
任何區間題目都有一個中國人能想到線段樹的做法
有長度\(n\)的陣列\(a\),\(q\)筆詢問,每次
1.詢問\([l,r]\)之區間和
2.將\(a_i\)改為\(x\)
\(n\leq 10^5,q\leq 10^5\)
暴力做太慢,用前綴和要修改也太慢...
最多修改\(O(\log n)\)個節點
最多查詢\(O(\log n)\)個節點
若當前節點存在\(seg[cur]\),則他的左子節點存在\(seg[cur*2]\),右子節存在\(seg[cur*2+1]\),從\(1\)開始
通常開\(seg[4*MAXN]\)
查詢、修改時遞迴處理
最好寫也最簡潔的一種
int seg[MAXN*4];
void modify(int l,int r,int cur,int ind,int val){
if(r-l<=1){
seg[cur]=val;
return;
}
int mid=(l+r)/2;
if(ind<mid) modify(l,mid,cur*2,ind,val);
else modify(mid,r,cur*2+1,ind,val);
seg[cur]=seg[cur*2]+seg[cur*2+1];
}
int query(int l,int r,int cur,int ql,int qr){
if(l>=qr||r<=ql) return 0;
if(ql<=l&&qr>=r) return seg[cur];
int mid=(l+r)/2;
return query(l,mid,cur*2,ql,qr)+query(mid,r,cur*2+1,ql,qr);
}
線段樹是一種將區間分塊的思想,這只是最基礎的一種應用,還有各式各樣的線段樹在等著各位。
之後資讀應該還有機會講。
BIT(Binary Indexed Tree),又稱Fenwick tree
可以算是線段樹的瘦身版,\(O(\log n)\)維護前綴和
區間合則需兩個前綴和相減