線段樹查詢區間最大最小值
阿新 • • 發佈:2019-02-06
#include <iostream> #include <set> #include <map> #include <stack> #include <cmath> #include <queue> #include <cstdio> #include <bitset> #include <string> #include <vector> #include <iomanip> #include <cstring> #include <algorithm> #include <functional> #define PI acos(-1) #define eps 1e-8 #define inf 0x3f3f3f3f #define debug(x) cout<<"---"<<x<<"---"<<endl typedef long long ll; using namespace std; #define maxn 200005 //元素總個數 #define ls l,m,rt<<1 #define rs m+1,r,rt<<1|1 int Sum[maxn << 2], Add[maxn << 2]; //Sum求和,Add為懶惰標記 int A[maxn], n; //存原陣列資料下標[1,n] ///建樹:Build(1,n,1); //PushUp函式更新節點資訊 ,這裡是求和 void PushUp(int rt) { Sum[rt] = max(Sum[rt << 1], Sum[rt << 1 | 1]);///^^^^^^^^^^^重點變化^^^^^^^^^^^^^^ } //Build函式建樹 void Build(int l, int r, int rt) //l,r表示當前節點區間,rt表示當前節點編號 { if (l == r) //若到達葉節點 { Sum[rt] = A[l]; //儲存陣列值 return; } int m = (l + r) >> 1; //左右遞迴 Build(l, m, rt << 1); Build(m + 1, r, rt << 1 | 1); //更新資訊 PushUp(rt); } ///點修改,假設A[L]+=C: Update(L,C,1,n,1); void Update(int L, int C, int l, int r, int rt) //l,r表示當前節點區間,rt表示當前節點編號 { if (l == r) //到葉節點,修改 { Sum[rt] += C; return; } int m = (l + r) >> 1; //根據條件判斷往左子樹呼叫還是往右 if (L <= m) { Update(L, C, l, m, rt << 1); } else { Update(L, C, m + 1, r, rt << 1 | 1); } PushUp(rt);//子節點更新了,所以本節點也需要更新資訊 } ///首先是下推標記的函式: void PushDown(int rt, int ln, int rn) { //ln,rn為左子樹,右子樹的數字數量。 if (Add[rt]) { //下推標記 Add[rt << 1] += Add[rt]; Add[rt << 1 | 1] += Add[rt]; //修改子節點的Sum使之與對應的Add相對應 Sum[rt << 1] += Add[rt] * ln; Sum[rt << 1 | 1] += Add[rt] * rn; //清除本節點標記 Add[rt] = 0; } } ///然後是區間查詢的函式:int ANS=Query(L,R,1,n,1); ^^^^^^^^^^查詢[L,R]區間的最大/最小值^^^^^^^^^^^^^ int Query(int L, int R, int l, int r, int rt) //L,R表示操作區間,l,r表示當前節點區間,rt表示當前節點編號 { if (L <= l && r <= R) { //在區間內,直接返回 return Sum[rt]; } int m = (l + r) >> 1; //下推標記,否則Sum可能不正確 PushDown(rt, m - l + 1, r - m); //累計答案 int ANS = 0; if (L <= m) { ANS = max(ANS, Query(L, R, l, m, rt << 1)); } if (R > m) { ANS = max(ANS, Query(L, R, m + 1, r, rt << 1 | 1)); } return ANS; }