高级数据布局-树状数组

[复制链接]
发表于 2025-12-7 11:41:29 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
先容




树状数组的推导


两个底子操纵


模板-acwing795. 前缀和

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N = 1e6+10;
  4. int c[N];
  5. int lowbit(int x){
  6.         return x & -x;
  7. }
  8. int query(int x){
  9.         int ans = 0;
  10.         for(; x; x -= lowbit(x)) ans += c[x];
  11.         return ans;
  12. }
  13. void add(int x,int y){
  14.         for(; x <= N; x += lowbit(x)) c[x] += y;
  15. }
  16. int main(){
  17.         int n,m; scanf("%d%d",&n,&m);
  18.         for(int i = 1; i <= n; i++){
  19.                 int num; scanf("%d",&num);
  20.                 add(i,num);
  21.         }
  22.         while(m--){
  23.                 int l,r; scanf("%d%d",&l,&r);
  24.                 printf("%d\n",query(r)-query(l-1));
  25.         }
  26.         return 0;
  27. }
复制代码
模板-acwing5910. 求逆序对

  1. ​
  2. #include<bits/stdc++.h>
  3. using namespace std;
  4. typedef long long LL;
  5. const int N = 1e6+10;
  6. LL c[N],a[N];
  7. int lowbit(int x)  // 返回末尾的1
  8. {
  9.     return x & -x;
  10. }
  11. LL query(int x){
  12.     LL ans = 0;
  13.     for(; x; x -= lowbit(x)) ans += c[x];
  14.     return ans;
  15. }
  16. void add(int x,int y){
  17.     for(; x <= N; x += lowbit(x)) c[x] += y;
  18. }
  19. int main()
  20. {
  21.     LL ans = 0;
  22.     int n; scanf("%d",&n);
  23.     for(int i = 1; i <= n; i++){
  24.         scanf("%d",&a[i]);
  25.     }
  26.     //倒序扫描,找值比当前这个数小但是先进入树状数组的数,即1-(a[i]-1)的和,加入ans(根据逆序对的定义构造的)
  27.     for(int i = n; i >= 1; i--){
  28.         ans += query(a[i]-1)-query(0);
  29.         add(a[i],1);
  30.     }
  31.     printf("%lld\n",ans);
  32.     return 0;
  33. }
  34. ​
复制代码
 P3374 【模板】树状数组 1 

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N = 1e6+10;
  4. long long c[N];
  5. int lowbit(int x){
  6.         return x & -x;
  7. }
  8. long long query(int x){
  9.         long long ans = 0;
  10.         for(; x; x -= lowbit(x)) ans += c[x];
  11.         return ans;
  12. }
  13. void add(int x,int y){
  14.         for(; x <= N; x += lowbit(x)) c[x] += y;
  15. }
  16. int main(){
  17.         long long n,m; scanf("%lld%lld",&n,&m);
  18.         for(int i = 1; i <= n; i++){
  19.                 long long x; scanf("%lld",&x);
  20.                 add(i,x);
  21.         }
  22.         while(m--){
  23.                 long long t,idx,x,l,r; scanf("%lld",&t);
  24.                 if(t == 1){
  25.                         scanf("%lld%lld",&idx,&x);
  26.                         add(idx,x);
  27.                 }else{
  28.                         scanf("%lld%lld",&l,&r);
  29.                         printf("%lld\n",query(r)-query(l-1));
  30.                 }
  31.         }
  32.         return 0;
  33. }
复制代码
P3368

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N = 1e6 + 10;
  4. int n, m, c[N],a[N];
  5. int lowbit(int x){
  6.         return x & -x;
  7. }
  8. void add(int x, int y){
  9.         for (int i = x; i <= n; i += lowbit(i)) c[i] += y;
  10. }
  11. int fn(int x){
  12.         int cnt = 0;
  13.         for (int i = x; i; i -= lowbit(i)) cnt += c[i];
  14.         return cnt;
  15. }
  16. int main()
  17. {
  18.         cin >> n >> m;
  19.         for (int i = 1; i <= n; i++){
  20.                 cin >> a[i];
  21.                 add(i, a[i] - a[i - 1]);
  22.         }
  23.        
  24.         while (m--){
  25.                 int t; cin >> t;
  26.                 if (t == 1){
  27.                         int x, y, k; cin >> x >> y >> k;
  28.                         add(x, k); add(y + 1, -k);
  29.                 }
  30.                 else {
  31.                         int x; cin >> x;
  32.                         cout << fn(x) << endl;
  33.                 }
  34.         }
  35.     return 0;
  36. }
复制代码
例题

A. 数列操纵


思绪

模板题,套树桩数组的模板即可。留意开long long
代码

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N = 1e6+10;
  4. long long c[N];
  5. int lowbit(int x){
  6.         return x & -x;
  7. }
  8. long long query(int x){
  9.         long long ans = 0;
  10.         for(; x; x -= lowbit(x)) ans += c[x];
  11.         return ans;
  12. }
  13. void add(int x,int y){
  14.         for(; x <= N; x += lowbit(x)) c[x] += y;
  15. }
  16. int main(){
  17.         long long n,m; scanf("%lld%lld",&n,&m);
  18.         for(int i = 1; i <= n; i++){
  19.                 long long x; scanf("%lld",&x);
  20.                 add(i,x);
  21.         }
  22.         while(m--){
  23.                 long long t,idx,x,l,r; scanf("%lld",&t);
  24.                 if(t == 1){
  25.                         scanf("%lld%lld",&idx,&x);
  26.                         add(idx,x);
  27.                 }else{
  28.                         scanf("%lld%lld",&l,&r);
  29.                         printf("%lld\n",query(r)-query(l-1));
  30.                 }
  31.         }
  32.         return 0;
  33. }
复制代码
 B. 数星星 Stars


思绪

        这道题的求解思绪跟逆序对那道题差不多,都是利用树状数组去查1-x的和,但是这道题要思量好一种雷同交心的思绪,就是他要找左下角的星星个数,他输入时是按照y增序,y雷同时按照x增序给出,那么某个星星的品级就是x坐标1-x的星星个数,但是要留意的是这道题有大概会坐标会为0那么就会出现越界题目,以是每次要给x坐标+1,但是团体每个数的巨细关系并不会改变。
代码

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N = 1e6+10;
  4. long long c[N],lv[N];
  5. int lowbit(int x){
  6.         return x & -x;
  7. }
  8. long long query(int x){
  9.         long long ans = 0;
  10.         for(; x; x -= lowbit(x)) ans += c[x];
  11.         return ans;
  12. }
  13. void add(int x,int y){
  14.         for(; x <= N; x += lowbit(x)) c[x] += y;
  15. }
  16. int main(){
  17.         int n; cin >> n;
  18.         for(int i = 1; i <= n; i++){
  19.                 int x,y; cin >> x >> y;
  20.                 x++;
  21.                 int t = query(x);
  22.                 lv[t]++;
  23.                 add(x,1);
  24.         }
  25.         for(int i = 0; i < n; i++) cout << lv[i] << endl;
  26.         return 0;
  27. }
复制代码
C. 校门外的树


思绪

        这道题由于是求某个区间内树的数目,树在区间内的具体位置是不知道的,以是用两个数组分别表现该点左侧树的数目和该点右侧树的数目,区间[a,b]内树的数目=b点左侧树的数目 – (a-1)点右侧树的数目。之以是是a-1点不是a点是由于要算上a点上的树。
代码

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. int const N = 50010;
  4. int n, m;
  5. // 树状数组模板
  6. int c1[N], c2[N];
  7. #define lowbit(x) (x & -x)
  8. typedef long long LL;
  9. void add(int c[], int x, int v) {
  10.     while (x < N) c[x] += v, x += lowbit(x);
  11. }
  12. LL sum(int c[], int x) {
  13.     LL res = 0;
  14.     while (x) res += c[x], x -= lowbit(x);
  15.     return res;
  16. }
  17. //这道题因为是求某个区间内树的数量,树在区间内的具体位置是不知道的
  18. //所以用两个数组分别表示该点左侧树的数量和该点右侧树的数量
  19. //区间[a,b]内树的数量=b点左侧树的数量 – (a-1)点右侧树的数量。之所以是a-1点不是a点是因为要算上a点上的树。
  20. int main() {
  21.     int k, l, r;
  22.     scanf("%d%d", &n, &m);
  23.     for (int i = 1; i <= m; i++) {
  24.         scanf("%d%d%d", &k, &l, &r);
  25.         if (k == 1)
  26.             add(c1, l, 1), add(c2, r, 1); // c1记录左括号的个数,c2记录右括号的个数
  27.         else
  28.             printf("%d\n", sum(c1, r) - sum(c2, l - 1));
  29.     }
  30.     return 0;
  31. }
复制代码
D. 盘货人数


思绪

        模板题,直接套模板。开long long
代码

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N = 1e6+10;
  4. long long c[N],lv[N];
  5. int lowbit(int x){
  6.         return x & -x;
  7. }
  8. long long query(int x){
  9.         long long ans = 0;
  10.         for(; x; x -= lowbit(x)) ans += c[x];
  11.         return ans;
  12. }
  13. void add(int x,int y){
  14.         for(; x <= N; x += lowbit(x)) c[x] += y;
  15. }
  16. int main(){
  17.         int n,k; cin >> n >> k;
  18.         for(int i = 1; i <= k; i++){
  19.                 char t;
  20.                 int m,p;
  21.                 cin >> t;
  22.                 if(t == 'A'){
  23.                         scanf("%d",&m);
  24.                         printf("%d\n",query(m));
  25.                 }else if(t == 'B'){
  26.                         scanf("%d%d",&m,&p);
  27.                         add(m,p);
  28.                 }else {
  29.                         scanf("%d%d",&m,&p);
  30.                         add(m,-p);
  31.                 }
  32.         }
  33.         return 0;
  34. }
复制代码
 E. 简单题


思绪

        这道题大概是这几道题中较难的一道,这道题如果不是取反操纵的话,就是一道树状数组区间修改区间查询的题了,但是他是取反操纵,我们就可以简化一下,不消像区间修改一样另开一个前缀和数组,接下来解说本体思绪:
        根据差分头脑,可以在从1~l先取反一次,再在1~r+1取反一次就可以得到l-r取反了,取反操纵就异或上1即可(^1),求某一位的值即从1~x的异或值就第x位的值。
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N = 1e6+10;
  4. bool c[N];
  5. int n,m;
  6. int lowbit(int x){
  7.         return x & -x;
  8. }
  9. bool query(int x){
  10.         bool ans = 0;
  11.         for(; x; x -= lowbit(x)) ans ^= c[x];
  12.         return ans;
  13. }
  14. void add(int x){
  15.         for(; x <= N; x += lowbit(x)) c[x] ^= 1;
  16. }
  17. void modify(int l, int r) // 原数组的区间修改 <- 差分数组的单点修改
  18. {
  19.         add(l);
  20.         if (r != n)
  21.                 add(r+1);
  22. }
  23. int main(){
  24.         cin >> n >> m;
  25.         for(int i = 1; i <= m; i++){
  26.                 char t;
  27.                 int x,l,r;
  28.                 cin >> t;
  29.                 if(t == '2'){
  30.                         scanf("%d",&x);
  31.                         printf("%d\n",query(x));
  32.                 }else{
  33.                         scanf("%d%d",&l,&r);
  34.                         modify(l,r);
  35.                 }
  36.         }
  37.         return 0;
  38. }
复制代码

回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表