线段树模板1 [Luogu P3372]

代码+注释:

 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 }

供个人复习使用

posted @ 2018-03-11 14:08  StupidJum  阅读(118)  评论(0编辑  收藏  举报