线段树(更新中)
[定义]:线段树是一种二叉树,也就是对于一个线段,我们会用一个二叉树来表示。
[性质]:结点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 }

浙公网安备 33010602011771号