[LuoguP1438]無聊的數列(差分+線段樹/樹狀數組)
阿新 • • 發佈:2018-07-23
!= lse 一個數 return 十分 ons 存在 void std ,從而我們可以通過\(dif\)反向得到\[base_i = \sum_{j = 1}^{i}{dif_j}\]吶,我們如果有區間加減這種操作或者其他的,我們可以通過操作\(dif_i\)和\(dif_{j + 1}\)來起到對區間\(i\)~\(j\)打標記的作用。關鍵就是一定要是單點查詢……區間查詢仿佛也可以做?但是有點麻煩略略略。
但是在程序實現的時候,筆者在此偷了個懶,沒有初始化\(dif\)數組,那麽我們就需要在區間查詢的時候改成這樣\[ans = \sum_{i = 1}^{P}{dif_i} + base_P\]
\(Link\)
\(\mathcal{Description}\)
給你一個數列,要求支持單點查詢\(and\)區間加等差數列。
\(\mathcal{Solution}\)
哈哈哈哈這個題十分的有意思,至於為什麽有意思等會兒再說~
其實我們觀察這兩個操作,單點查詢……就是那個\(naive\)的單點查詢,那麽區間加等差數列呢?我們可以思考一下等差數列的性質——存在公差。不妨考慮差分
\(emmm\)發現我好像還沒有在博客園裏提過差分……那麽就整一整吧正好我好久沒捯飭這玩意兒了\(qwq\)
差分
其實就是對於一個給定的數列\(base\),我們用另一個數組\(dif_i\)記錄\(base_i - base_{i - 1}\)
回到這個題,我們的線段樹可以建在數列的差分數組上。然後區間加等差數列的時候,我們就讓\(dif_L += D\),\(dif_{L+1...R} += K\),\(dif_{R+1} -= (K \times (R - L) + D)\)很顯然。如果要是區間查詢的話,我們就直接線段樹求個\[ans = \sum_{i = 1}^{P}{dif_i}\]
\(Code\)
#include <cstdio> #include <iostream> #define mid ((l + r) >> 1) using namespace std ; const int MAXN = 100050 ; int N, M, P, mark, i, base[MAXN] ; int L, R, K, D, dif[MAXN << 2], tag[MAXN << 2] ; inline int qrd(){ int k = 0, f = 1 ; char c = getchar() ; while(!isdigit(c)) {if(c == ‘-‘) f = -1; c = getchar() ;} while(isdigit(c)) k = (k << 1) + (k << 3) + c - 48, c = getchar() ; return k * f ; } inline void p_u(int rt){dif[rt] = dif[rt << 1] + dif[rt << 1 | 1] ;} inline void p_d(int rt, int l, int r){ if(tag[rt]){ dif[rt << 1] += tag[rt] * (mid - l + 1) ; dif[rt << 1 | 1] += tag[rt] * (r - mid) ; tag[rt << 1] += tag[rt] ; tag[rt << 1 | 1] += tag[rt] ; tag[rt] = 0 ; } } void update(int rt, int l, int r, int ul, int ur, int k){ if(ul <= l && r <= ur){ tag[rt] += k ; dif[rt] += k * (r - l + 1) ; return ; }p_d(rt, l, r) ; if(ul <= mid) update(rt << 1, l, mid, ul, ur, k) ; if(ur > mid) update(rt << 1 | 1, mid + 1, r, ul, ur, k) ; p_u(rt) ; } int query(int rt, int l, int r, int ql, int qr){ if(ql <= l && r <= qr){return dif[rt] ;}p_d(rt, l, r) ; int res = 0 ; if(ql <= mid) res += query(rt << 1, l, mid, ql, qr) ; if(qr > mid) res += query(rt << 1 | 1, mid + 1, r, ql, qr) ; return res ; } int main(){ N = qrd(), M = qrd() ; for(i = 1; i <= N; i ++) base[i] = qrd() ; for(i = 1; i <= M; i ++){ cin >> mark ; if (mark == 1) { L = qrd(), R = qrd(), K = qrd(), D = qrd() ; update(1, 1, N, L, L, K) ; if (R > L) update(1, 1, N, L + 1, R, D) ; if (R != N) update(1, 1, N, R + 1, R + 1, -(R - L) * D - K) ; } else { P = qrd() ; cout << base[P] + query(1, 1, N, 1, P) << endl ; } } }
[LuoguP1438]無聊的數列(差分+線段樹/樹狀數組)