区间修改,查询之树状数组
方法:
①单点修改,单点查询
(这个没什么好说的,就是单纯的数组)
②单点修改,区间查询
树状数组:树状数组能单点修改,并且区间1 ~ n的和,区间查询只需要用到前缀和的思想就可以了。
线段树:√
③区间修改,单点查询
差分:区间修改只需改变两个值,单点查询时求一遍前缀和。
树状数组 + 差分:在进行修改的时候导入的数是差分的值,最后树状数组求和其实就是求一遍前缀和答案过程。
线段树:√
④区间修改,区间查询
树状数组:区间修改依旧是要将原数组进行差分。区间查询需要维护两个前缀和数组。
原本的差分数组为b数组,b的前缀和数组是a数组。
线段树:√
复杂度:
差分:修改$O(1)$, 查询$O(n)$。
树状数组:修改$O(logn)$, 查询$0(logn)$。
线段树:修改$O(logn)$, 查询$0(logn)$。
https://www.acwing.com/problem/content/243/
acwing 241.楼兰图腾
树状数组处理大于小于数字的个数

该题的思路就是预处理每一个点前面大于该点的数量,后面大于该点的数量,相乘即为V的个数。同理,对于倒V也是一样,处理的是小于的值。
容易想到用树状数组做,先从正面扫一遍,预处理两个数组up和down。然后再从后面扫一遍,用之前预处理的数组与之相乘即为答案。时间复杂度$O(nlogn)$。
1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 5 using namespace std; 6 7 typedef long long LL; 8 9 const int N = 200010; 10 11 int tr[N], a[N]; 12 int up[N], down[N]; 13 int n; 14 15 int lowbit(int x) 16 { 17 return x & -x; 18 } 19 20 void add(int x, int c) 21 { 22 for(int i = x ; i <= n ; i += lowbit(i))tr[i] += c; 23 } 24 25 int sum(int x) 26 { 27 int res = 0; 28 for(int i = x ; i ; i -= lowbit(i))res += tr[i]; 29 return res; 30 } 31 32 int main(){ 33 cin >> n; 34 for(int i = 1 ; i <= n ; i ++)cin >> a[i]; 35 36 for(int i = 1 ; i <= n ; i ++) 37 { 38 int y = a[i]; 39 up[i] = sum(n) - sum(y); 40 down[i] = sum(y - 1); 41 add(y, 1); 42 } 43 44 memset(tr, 0, sizeof tr); 45 46 LL res1 = 0, res2 = 0; 47 for(int i = n ; i >= 1 ; i --) 48 { 49 int y = a[i]; 50 res1 += up[i] * ((LL)sum(n) - sum(y)); 51 res2 += down[i] * ((LL)sum(y - 1)); 52 add(y, 1); 53 } 54 55 cout << res1 << " " << res2 << endl; 56 return 0; 57 }
https://www.acwing.com/problem/content/248/
acwing 242.一个简单的整数问题
树状数组处理区间相加,单点查询

差分与树状数组的结合,树状数组本身可以求解单点修改,区间查询的任务。和差分结合起来,将树状数组中存储的值改为差分的值,那么就可以求解区间修改,单点查询的任务。
1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 5 using namespace std; 6 7 const int N = 1e5+10; 8 9 int a[N], tr[N]; 10 int n, m; 11 12 int lowbit(int x) 13 { 14 return x & -x; 15 } 16 17 void add(int x, int c) 18 { 19 for(int i = x ; i <= n ; i += lowbit(i))tr[i] += c; 20 } 21 22 int sum(int x) 23 { 24 int res = 0; 25 for(int i = x ; i ; i -= lowbit(i))res += tr[i]; 26 return res; 27 } 28 29 int main(){ 30 cin >> n >> m; 31 32 for(int i = 1 ; i <= n ; i ++)cin >> a[i]; 33 34 for(int i = 1 ; i <= n ; i ++)add(i, a[i] - a[i - 1]); 35 36 while(m --) 37 { 38 char op[2]; 39 cin >> op; 40 if(op[0] == 'Q') 41 { 42 int a; 43 cin >> a; 44 cout << sum(a) << endl; 45 } 46 else 47 { 48 int a, b, c; 49 cin >> a >> b >> c; 50 add(a, c); 51 add(b + 1, -c); 52 } 53 } 54 55 return 0; 56 }
https://www.acwing.com/activity/content/problem/content/1594/1/
acwing 243.一个简单的整数问题2
差分+前缀和+树状数组构造求解区间修改区间查询
b为a的差分数组,同样,对于单点查询查询点x来说为$\sum_{i = 1}^{x}b_i$,所以对于区间$1-x$的查询为$\sum_{j = 1}^{x}\sum_{i = 1}^{j}b_i$。
显然这是不能直接求解的。将其的各项罗列出来:
$b_1$
$b_1 + b_2$
$b_1 + b_2 + b_3$
$\cdot \cdot\cdot \cdot\cdot \cdot$
$b_1 + b_2+b_3 + b_4 +\cdot \cdot\cdot \cdot b_x$
然后将其对称:
${\color{Blue}{b_1 + b_2 + b_3+b_4 +\cdot \cdot\cdot \cdot b_x} }$
$b_1+{\color{Blue}{b_2 + b_3+b_4 +\cdot \cdot\cdot \cdot b_x} }$
$b_1 + b_2+{\color{Blue}{b_3+b_4 +\cdot \cdot\cdot \cdot b_x }}$
$b_1 + b_2 + b_3+{\color{Blue}{b_4 +\cdot \cdot\cdot \cdot b_x }}$
$\cdot \cdot\cdot \cdot\cdot \cdot$
$b_1 + b_2+b_3 + b_4 +\cdot \cdot\cdot \cdot b_x$
所以黑色的部分为我们要求的和,那么整个方形相加的和为$x*sum_1(x)$, 所求的面积的和是$x * sum_1(x) - (b_1 + 2b_2 + 3b_3 +\cdot \cdot\cdot \cdot + xb_x)$。
因此我们再处理一个差分数组是$ib_i$的树状数组,区间查询$1-x$的结果就为$x * sum_1(x) - sum_2(x)$。
树状数组代码:
1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 5 using namespace std; 6 7 typedef long long LL; 8 9 const int N = 1e5+10; 10 11 int a[N]; 12 LL tr1[N], tr2[N]; 13 int n, m; 14 15 int lowbit(int x) 16 { 17 return x & -x; 18 } 19 20 void add(LL tr[], int x, LL c) 21 { 22 for(int i = x ; i <= n ; i +=lowbit(i))tr[i] += c; 23 } 24 25 LL sum(LL tr[], int x) 26 { 27 LL res = 0; 28 for(int i = x ; i ; i -= lowbit(i))res += tr[i]; 29 return res; 30 } 31 32 LL get_sum(int x) 33 { 34 return sum(tr1, x) * (x + 1) - sum(tr2, x); 35 } 36 37 int main() 38 { 39 cin >> n >> m; 40 41 for(int i = 1 ; i <= n ; i ++)cin >> a[i]; 42 43 for(int i = 1 ; i <= n ; i ++) 44 { 45 int b = a[i] - a[i - 1]; 46 add(tr1, i, b); 47 add(tr2, i, (LL)b * i); 48 } 49 50 while(m --) 51 { 52 char op[2]; 53 cin >> op; 54 if(op[0] == 'Q') 55 { 56 int a, b; 57 cin >> a >> b; 58 cout << get_sum(b) - get_sum(a - 1) << endl; 59 60 } 61 else 62 { 63 int a, b, c; 64 cin >> a >> b >> c; 65 add(tr1, a, c); 66 add(tr1, b + 1, -c); 67 add(tr2, a, c * a); 68 add(tr2, b + 1, -c * (b + 1)); 69 } 70 } 71 72 return 0; 73 }
线段树代码:
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 5 using namespace std; 6 7 typedef long long LL; 8 9 const int N = 100010; 10 11 int n, m; 12 int w[N]; 13 struct Node{ 14 int l, r; 15 LL sum, add; 16 }tr[4 * N]; 17 18 void pushup(int u) 19 { 20 tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum; 21 } 22 23 void pushdown(int u) 24 { 25 Node &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1]; 26 if(root.add) 27 { 28 left.add += root.add, left.sum += (LL)(left.r - left.l + 1) * root.add; 29 right.add += root.add, right.sum += (LL)(right.r - right.l + 1) * root.add; 30 root.add = 0; 31 } 32 } 33 34 void build(int u, int l, int r) 35 { 36 if(l == r)tr[u] = {l, r, w[l], 0}; 37 else 38 { 39 tr[u] = {l, r}; 40 int mid = l + r >> 1; 41 build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r); 42 pushup(u); 43 } 44 } 45 46 void modify(int u, int l, int r, int d) 47 { 48 if(l <= tr[u].l && tr[u].r <= r) 49 { 50 tr[u].sum += (LL)(tr[u].r - tr[u].l + 1) * d; 51 tr[u].add += d; 52 } 53 else 54 { 55 pushdown(u); 56 int mid = tr[u].l + tr[u].r >> 1; 57 if(l <= mid)modify(u << 1, l, r, d); 58 if(r > mid)modify(u << 1 | 1, l, r, d); 59 pushup(u); 60 } 61 } 62 63 LL query(int u, int l, int r) 64 { 65 if(l <= tr[u].l && tr[u].r <= r)return tr[u].sum; 66 67 pushdown(u); 68 int mid = tr[u].l + tr[u].r >> 1; 69 LL sum = 0; 70 if(l <= mid)sum = query(u << 1, l, r); 71 if(r > mid)sum += query(u << 1 | 1, l, r); 72 return sum; 73 } 74 75 76 int main(){ 77 scanf("%d%d", &n, &m); 78 for(int i = 1 ; i <= n ; i ++)scanf("%d", &w[i]); 79 80 build(1, 1, n); 81 82 while(m --) 83 { 84 char op[2]; 85 int l, r; 86 scanf("%s%d%d", op, &l, &r); 87 if(*op == 'C') 88 { 89 int d; 90 scanf("%d", &d); 91 modify(1, l, r, d); 92 } 93 else printf("%lld\n", query(1, l, r)); 94 } 95 96 return 0; 97 }

浙公网安备 33010602011771号