目錄對你說:我在右邊
若是你不會線段樹,戳這裏html
這就是push_up()淺顯易懂.node
void push_up(int rt) { tree[rt].max = max(tree[lson].max, tree[rson].max); tree[rt].min = min(tree[lson].min, tree[rson].min); }
建樹的時候就那樣建,push_down的時候看一下max和min都改爲lazy就好了.
有的時候用不到push_down();數組
也很好懂,和求區間和差很少.
有修改操做的時候能夠加上pushd_down();數據結構
int query_max(int rt, int l, int r, int L, int R) { if (L <= l && r <= R) return tree[rt].max; int mid = (l + r) >> 1, maxn = -1; if (L <= mid) maxn = max(maxn, query_max(lson, l, mid, L, R)); if (R > mid) maxn = max(maxn, query_max(rson, mid + 1, r, L, R)); return maxn; }
這是個好題
強烈推薦GSS毒瘤數據結構,在洛谷能夠搜到,作完以後會感受本身的思惟獲得了昇華.
A一題提神醒腦,A倆題永不疲勞,A仨題長生不老
由於一個大整數\(\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{n}}}}}} = 1\)咱們能夠用lazy判斷一下這個區間開了幾回,若是大於等於6的話就不必搞了(然而我沒寫)
並且開方的時候判斷一下區間的最大值是否是1,若是區間的最大值都是1了,那麼其餘的必然也是1,就沒有必要在開方了.spa
void update(int L, int R, int l, int r, int rt) { if (l == r) { t[rt].sum = sqrt(t[rt].sum); t[rt].mx = sqrt(t[rt].mx); return ; } int m = (l + r) >> 1; if (L <= m && t[lson].mx > 1) update(L, R, l, m, lson); if (R > m && t[rson].mx > 1) update(L, R, m + 1, r, rson); pushup(rt); }
查詢就不用說的吧,和區間求最大值同樣啊。code
好題 能夠說是裸題.
難點在於push_up和queryhtm
void push_up(int rt) { tree[rt].sum = tree[lson].sum + tree[rson].sum;//這就是普通的區間和 tree[rt].qian = max(tree[lson].qian, tree[lson].sum + tree[rson].qian); tree[rt].hou = max(tree[rson].hou, tree[rson].sum + tree[lson].hou); //區間的前綴和的最大值就是左區間的前綴和和(右區間的全綴合加上左區間的和)取max //區間後綴和類比可得 tree[rt].zi = max(tree[lson].zi, tree[rson].zi); tree[rt].zi = max(tree[rt].zi, tree[lson].hou + tree[rson].qian); //最大子段和就是左右區間最大子段和,還有左區間後綴和加上右區間前綴和取max }
node unioc(node a, node b) { node ans;//區間合併可類比push_up ans.sum = a.sum + b.sum; ans.zi = max(a.zi, b.zi); ans.zi = max(a.hou + b.qian, ans.zi); ans.qian = max(a.qian, a.sum + b.qian); ans.hou = max(b.hou, b.sum + a.hou); return ans; } node query(int rt, int l, int r, int L, int R) { if (L <= l && r <= R) return tree[rt]; int mid = (l + r) >> 1; if (L > mid) return query(rson, mid + 1, r, L, R);//若是這個區間都在右邊,直接在右邊算就行 if (R <= mid) return query(lson, l, mid, L, R);//爭端區間都在左邊 return unioc(query(lson, l, mid, L, R), query(rson, mid + 1, r, L, R));//若是兩端都有那麼還要合併區間. }
加上區間修改就變成了這個題
也挺簡單的,只要你會了上邊那個.
區間修改也挺簡單的.通俗易懂。blog
void change(int rt, int c, int l, int r, int pos) { if (l == r) { tree[rt].sum = tree[rt].ls = tree[rt].rs = tree[rt].mis = c; return; } int mid = (l + r) >> 1; if (pos <= mid) change(lson, c, l, mid, pos); if (pos > mid) change(rson, c, mid + 1, r, pos); push_up(rt); }
咱們都知道\(gcd(a, b) = gcd(b, a-b)\)(更相減損術)
咱們將上述式子擴展到3項咱們就會發現
\(gcd(a,b,c)=gcd(b,b−a,c−b)\)
很明顯這就是一個差分數組
咱們只須要開一棵線段樹來維護差分數組gcd
可是咱們發現最前邊的這個b咱們維護的是當前這個點的差分值
因此這就說明咱們還須要開一個東西來維護每個點的值
並且還要支持區間修改(樹狀數組是一個好東西)get
其實和求區間最大值差很少it
void push_up(int rt) { tree[rt].gcd = abs(gcd(tree[lson].gcd, tree[rson].gcd)); }
咱們須要查詢一個最前邊的值的大小,還有剩下的值的gcd
而後二者求一個gcd
ll query(int rt, int l, int r, int L, int R) { if (L <= l && r <= R) return tree[rt].gcd; int mid = (l + r) >> 1;ll g = 0; if (L <= mid) g = abs(gcd(g, query(lson, l, mid, L, R))); if (R > mid) g = abs(gcd(g, query(rson, mid + 1, r, L, R))); return g; } ll ask(int b) { ll ans = 0; while (b) ans += t[b], b -= lowbit(b); return ans; } x = read(), y = read(); ll ans = query(1, 1, n + 1, x + 1, y); printf("%lld\n", abs(gcd(ans, ask(x))));
之後作線段樹的時候考慮怎麼上傳,怎麼下放,區間查詢的時候需不須要合併.
合併的時候須要注意什麼問題.
【未完待續】