1. 程式人生 > >線段樹模板1 [Luogu P3372]

線段樹模板1 [Luogu P3372]

str 填充 namespace 清空 flag pan 斷開 nod 點數據

代碼+註釋:

 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 
 5 int n, q, flag, x, y, v;
 6 int a[100010];
 7 struct node{
 8     long long a, b;
 9 }tree[400010];    //樹,a為數據域,b為標記
10 
11 void build_tree(int now, int l, int r) {    //建樹
12     if (l == r) {
13         tree[now].a = a[l];            //
填充數據 14 return ; 15 } 16 int mid = (l + r) >> 1; //mid = (l + r) div 2; 位運算加速 17 build_tree(now + now, l, mid); //往左邊繼續建樹 18 build_tree(now + now + 1, mid + 1, r); //往右邊繼續建樹 19 tree[now].a = tree[now + now].a + tree[now + now + 1].a; //此時節點數據域為下接兩個節點的和,便於查詢
20 } 21 22 void plust(int now, int l, int r, int x, int y) {//區間加操作 23 if ((l == x) && (r == y) || (l == r)) { //達到要求 24 tree[now].a += v * (r - l + 1); //累加v的值與當前節點下所有的節點數的積(若當前節點下沒有節點則為1) 25 tree[now].b += v; //打標記 26 return ; //回溯 27 }
28 int mid = (l + r) >> 1; 29 tree[now + now].b += tree[now].b; //向下傳遞標記(以下都是) 30 tree[now + now + 1].b += tree[now].b; 31 tree[now + now].a += (tree[now].b * (mid - l + 1)); 32 tree[now + now + 1].a += (tree[now].b * (r - mid)); 33 tree[now].b = 0; //向下傳遞標記,當前標記清空(不管怎麽樣就是這樣) 34 if (y <= mid) plust(now + now, l, mid, x, y);//若操作區間在mid左邊,往左邊繼續操作 35 else if (x > mid) plust(now + now + 1, mid + 1, r, x, y);//若在mid右邊,往右邊繼續操作 36 else { //否則拆開分別左右操作 37 plust(now + now, l, mid, x, mid); 38 plust(now + now + 1, mid + 1, r, mid + 1, y); 39 } 40 tree[now].a = tree[now + now].a + tree[now + now + 1].a; //更新當前節點的數據 41 } 42 43 long long query(int now, int l, int r, int x, int y) { //區間查詢 44 if ((l == x) && (r == y)) return tree[now].a; //若當前節點所代表的區間恰好與需要查詢的區間重合,返回值 45 int mid = (l + r) >> 1; 46 tree[now + now].b += tree[now].b; //同上,向下傳遞標記(以下都是) 47 tree[now + now + 1].b += tree[now].b; 48 tree[now + now].a += (tree[now].b * (mid - l + 1)); 49 tree[now + now + 1].a += (tree[now].b * (r - mid)); 50 tree[now].b = 0; //同上,向下傳遞標記 51 if (y <= mid) return query(now + now, l, mid, x, y); //查詢區間在mid左邊,往左邊取 52 else if (x > mid) return query(now + now + 1, mid + 1, r, x, y); //否則在右邊取 53 else return query(now + now, l, mid, x, mid) + query(now + now + 1, mid + 1, r, mid + 1, y); //再否則斷開從左右邊取 54 } 55 56 int main() { //主程序 57 scanf("%d%d", &n, &q); //讀入數列的長度和操作的次數 58 for (int i = 1; i <= n; i++) 59 scanf("%d", &a[i]); 60 61 build_tree(1, 1, n); //建樹 62 63 for (int i = 1; i <= q; i++) { 64 scanf("%d%d%d", &flag, &x, &y); //開始 65 if (flag == 1) { 66 scanf("%d", &v); //v為要加進去的數 67 plust(1, 1, n, x, y); //區間加操作 68 continue; 69 } 70 printf("%lld\n", query(1, 1, n, x, y)); //否則查詢區間 71 } 72 73 return 0; 74 }

供個人復習使用

線段樹模板1 [Luogu P3372]