线段树(更新中)

[定义]:线段树是一种二叉树,也就是对于一个线段,我们会用一个二叉树来表示。
[性质]:结点i的权值 = i左儿子的权值 + i右儿子的权值
[代码实现]:
根据性质可以创建结构体 tree 
1 struct tree
2 {
3     int l;//左区间下标
4     int r;//右区间下标
5     int sum;//结点线段和
6     int add;//懒标记
7 };
那么上述性质可以表述为
tree[i].sum = tree[i * 2].sum + tree[i * 2 + 1].sum;
 1 inline void buildtree(int i, int l, int r)//递归建树
 2 {
 3     tree[i].l = l;
 4     tree[i].r = r;
 5     if (l == r)//如果结点为叶子结点
 6     {
 7         tree[i].sum = arr[l];//原数组赋值
 8         return;
 9     }
10     int mid = (l + r) / 2;
11     //构造左右子树
12     buildtree(i * 2, l , mid);
13     buildtree(i * 2 + 1, mid + 1, r);
14     tree[i].sum = tree[i * 2].sum + tree[i * 2 + 1].sum;
15 }
[线段树的操作]:
0.懒标记传递
 1 inline void pushdown(int i)
 2 {
 3     if (tree[i].add)//如果懒标记不为0,就将其下传,修改左右儿子维护的值 
 4     {
 5         tree[i << 1].sum += tree[i].add * (tree[i << 1].r - tree[i << 1].l + 1);
 6         tree[i << 1 | 1].sum += tree[i].add * (tree[i << 1 | 1].r - tree[i << 1 | 1].l + 1);
 7         //为该结点的左右儿子打上标记 
 8         tree[i << 1].add += tree[i].add;
 9         tree[i << 1 | 1].add += tree[i].add;
10         tree[i].add = 0;//下传之后将该结点的懒标记清0 
11     }
12 }
1.单点修改
(1)从根结点向下查询,查找到叶子结点更新
(2)利用性质一步步向上更新
 1 inline void update_single(int i, int dis, int val)//dis表示要修改的点;val表示更新值
 2 {
 3     if (tree[i].l == tree[i].r)
 4     {
 5         tree[i].sum += val;
 6         tree[i].add += val;
 7         return;
 8     }
 9     pushdown(i);
10     int mid = (tree[i].l + tree[i].r) >> 1;
11     if (dis <= mid)
12         update_single(i * 2, dis, val);
13     else 
14         update_single(i * 2 + 1, dis, val);
15     tree[i].sum = tree[i * 2].sum + tree[i * 2 + 1].sum;
16 }
2.区间修改
 1 inline void update_area(int i, int l, int r, int val)
 2 {
 3     if (tree[i].l >= l && tree[i].r <= r)//如果覆盖的话,就对其进行修改 
 4     {
 5         tree[i].sum += val * (tree[i].r - tree[i].l + 1);
 6         tree[i].add += val;//打上懒标记 
 7         return; 
 8     }
 9     //如果发现没有被覆盖,那就需要继续向下找,考虑儿子所维护的区间可能因为懒标记的存在而没有修改
10     //因此将懒标记下放 
11     pushdown(i); 
12     int mid = (tree[i].l + tree[i].r) >> 1;
13     if (l <= mid)
14         update_area(i << 1, l , r, val);
15     if (r > mid)
16         update_area(i << 1 | 1, l, r, val);
17     tree[i].sum = tree[i << 1].sum + tree[i << 1 | 1].sum; 
18 }
3.单点查询
 1 inline int search_single(int i, int dis)
 2 {
 3     if (tree[i].l > dis || tree[i].r < dis)
 4         return 0;
 5     if (tree[i].l == tree[i].r)
 6         return tree[i].sum;
 7     pushdown(i);
 8     int mid = (tree[i].l + tree[i].r) >> 1;
 9     if (dis <= mid)
10         search_single(i << 1,dis);
11     else
12         search_single(i << 1 | 1,dis);
13 }
4.区间查询
区间查询:
(1)如果这个区间被完全包括在目标区间里面,直接返回这个区间的值
(2)如果这个区间的左儿子和目标有交集,那么搜索左儿子
(3)如果这个区间的右儿子和目标区间有交集,那么搜索右儿子
 1 inline int search_area(int i, int l, int r)//l,r表示待查找区间的左右下标
 2 {
 3     if (tree[i].r < l && tree[i].l > r)//如果这个区间和目标区间毫不相干,返回0
 4         return 0;
 5     if (tree[i].l >= l && tree[i].r <= r)//(1)
 6         return tree[i].sum;
 7     pushdown(i);
 8     int mid = (tree[i].l + tree[i].r) >> 1;
 9     ll sum = 0;
10     if (l <= mid)//(2)
11         sum += search_area(i << 1, l, r);
12     if (r > mid)//(3)
13         sum += search_area(i << 1 | 1, l , r);
14     return sum;
15 }
[模板1 区间查询 + 区间更新] P3372
  1 #include <cstdio>
  2 #include <iostream>
  3 #include <cstring>
  4 #include <cmath>
  5 #include <cstdlib>
  6 #include <string>
  7 #include <vector>
  8 #include <queue>
  9 #include <map>
 10 #include <set>
 11 #include <algorithm>
 12 #define rep(i,s,n) for (int i = s; i <= n; ++i)
 13 #define fi first
 14 #define se second
 15 using namespace std;
 16 typedef long long ll;
 17 typedef pair<int,int> pii;
 18 const int MAXN = 1e5 + 10; 
 19 const int INF = 0x3f3f3f3f;
 20 int n,m;
 21 int c,l,r;
 22 ll val;
 23 int arr[MAXN];
 24 struct Tree
 25 {
 26     int l;
 27     int r;
 28     ll sum;
 29     ll add;//懒标记 
 30 }tree[MAXN << 2];
 31 inline void buildtree(int i, int l, int r)
 32 {
 33     tree[i].l = l;
 34     tree[i].r = r;
 35     if (l == r)
 36     {
 37         tree[i].sum = arr[l];
 38         return;
 39     }
 40     int mid = (l + r) >> 1;
 41     buildtree(i << 1, l, mid);
 42     buildtree(i << 1 | 1, mid + 1, r);
 43     tree[i].sum = tree[i << 1].sum + tree[i << 1 | 1].sum;
 44 }
 45 /*
 46 * 懒标记
 47 * P3372中为了使所有的sum值都保持正确,每一次插入操作可能要更新O(N)个sum值,从使时间复杂度
 48 * 退化为O(N)
 49 * 解决方案是Lazy思想:对整个结点进行的操作,先在结点上做标记,而并非真正执行,直到根据查询
 50 * 操作的需要分成两部分。 
 51 */
 52 inline void pushdown(int i)
 53 {
 54     if (tree[i].add)//如果懒标记不为0,就将其下传,修改左右儿子维护的值 
 55     {
 56         tree[i << 1].sum += tree[i].add * (tree[i << 1].r - tree[i << 1].l + 1);
 57         tree[i << 1 | 1].sum += tree[i].add * (tree[i << 1 | 1].r - tree[i << 1 | 1].l + 1);
 58         //为该结点的左右儿子打上标记 
 59         tree[i << 1].add += tree[i].add;
 60         tree[i << 1 | 1].add += tree[i].add;
 61         tree[i].add = 0;//下传之后将该结点的懒标记清0 
 62     }
 63 }
 64 inline void update_area(int i, int l, int r, ll val)
 65 {
 66     if (tree[i].l >= l && tree[i].r <= r)//如果覆盖的话,就对其进行修改 
 67     {
 68         tree[i].sum += val * (tree[i].r - tree[i].l + 1);
 69         tree[i].add += val;//打上懒标记 
 70         return; 
 71     }
 72     //如果发现没有被覆盖,那就需要继续向下找,考虑儿子所维护的区间可能因为懒标记的存在而没有修改
 73     //因此将懒标记下放 
 74     pushdown(i); 
 75     int mid = (tree[i].l + tree[i].r) >> 1;
 76     if (l <= mid)
 77         update_area(i << 1, l , r, val);
 78     if (r > mid)
 79         update_area(i << 1 | 1, l, r, val);
 80     tree[i].sum = tree[i << 1].sum + tree[i << 1 | 1].sum; 
 81 }
 82 inline ll search_area(int i, int l, int r)
 83 {
 84     if (tree[i].r < l && tree[i].l > r)
 85         return 0;
 86     if (tree[i].l >= l && tree[i].r <= r)
 87         return tree[i].sum;
 88     pushdown(i);
 89     int mid = (tree[i].l + tree[i].r) >> 1;
 90     ll sum = 0;
 91     if (l <= mid)
 92         sum += search_area(i << 1, l, r);
 93     if (r > mid)
 94         sum += search_area(i << 1 | 1, l , r);
 95     return sum;
 96 }
 97 int main()
 98 {
 99     scanf("%d%d",&n,&m);
100     rep(i,1,n)
101         scanf("%d",&arr[i]);
102     buildtree(1,1,n);
103     rep(i,1,m)
104     {
105         scanf("%d",&c);
106         if (c == 1)
107         {
108             scanf("%d%d%lld",&l,&r,&val);
109             update_area(1,l,r,val);
110         }
111         else
112         {
113             scanf("%d%d",&l,&r);
114             ll ans = search_area(1,l,r);
115             printf("%lld\n",ans);
116         }    
117     }
118     return 0;
119 }

 

posted @ 2021-07-29 12:00  QingHUIWuHan  阅读(66)  评论(0)    收藏  举报