区间修改,查询之树状数组

方法:

①单点修改,单点查询

  (这个没什么好说的,就是单纯的数组)

②单点修改,区间查询

  树状数组:树状数组能单点修改,并且区间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 }

 

posted @ 2020-03-25 13:42  dzcixy  阅读(501)  评论(0)    收藏  举报