树状数组的原理和基础应用

这样的数据结构称作树状数组,它支持O(logN)的单点修改和区间查询,效率高并且代码简洁,缺点在于适用范围不如线段树广。
不难看出(雾),tree[i]表示a[i]及之前的 lowbit(i)个 数,定义lowbit(i)等于取i的二进制中最后一个‘1‘表示的大小
观察发现(。),修改a[i]只需更新包含i的节点,倒推可知从i开始,每次把i加上lowbit(i),这些节点包含了a[i]
从i开始,每次把i减去lowbit(i)直到为0,这些节点的值加起来就是前缀和。
例题1:树状数组1  https://www.luogu.com.cn/problem/P3374
题意:单点修改,区间查询

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,m,opt,k,x,y,a,tree[500005];
 4 int lowbit(int i){
 5     return i&-i;
 6 }
 7 void add(int i,int a){
 8     for(;i<=n;i+=lowbit(i))
 9     tree[i]+=a;
10 }
11 int sum(int i){
12     int s=0;
13     for(;i;i-=lowbit(i))
14       s+=tree[i];
15   return s;
16 }
17 int main(){
18     //freopen("out.txt","w",stdout);
19   scanf("%d%d",&n,&m);
20     for(int i=1;i<=n;i++)    scanf("%d",&a),add(i,a);
21     while(m--){
22         scanf("%d",&opt);
23         if(opt==1){
24             scanf("%d%d",&x,&k);
25             add(x,k);
26         }
27         if(opt==2){
28             scanf("%d%d",&x,&y);
29             printf("%d\n",sum(y)-sum(x-1));
30         }
31     }
32 }

例题2 :树状数组2 https://www.luogu.com.cn/problem/P3368
题意:区间修改,区间查询
题解:常用技巧:用树状数组维护差分数组,查询时快速前缀和

 1 #include<bits/stdc++.h>
 2 int n,m,a,opt,x,y,k,d[500005],tree[500005];
 3 int lowbit(int i){
 4     return i&-i;
 5 }
 6 void add(int i,int a){
 7     for(;i<=n;i+=lowbit(i))  tree[i]+=a;
 8 }
 9 int sum(int i){
10     int s=0;
11     for(;i;i-=lowbit(i))s+=tree[i];
12     return s;
13 }
14 int main(){
15     scanf("%d%d",&n,&m);
16     for(int i=1;i<=n;i++)scanf("%d",&d[i]),add(i,d[i]-d[i-1]);
17     while(m--){
18         scanf("%d",&opt);
19         if(opt==1){
20             scanf("%d%d%d",&x,&y,&k);
21             add(x,k);add(y+1,-k);
22         }
23         if(opt==2){
24             scanf("%d",&x);
25             printf("%d\n",sum(x));
26         }
27     }
28 }

例题3 [SDOI2009]HH的项链https://www.luogu.com.cn/problem/P1972
题意:给出一串项链中每一个贝壳属于哪一种,给出m组询问,求给定区间内贝壳的种类数。
题解:离线思想,把询问按右端点排序并记录原来的顺序。
记录上一次查到了last-1,那么这一次只需更新last到q[i].r,如果这某一种之前出现过就在之前的节点减一。把现在的节点加一。
然后把前缀和一减就是区间和。

 1 #include<bits/stdc++.h>
 2 #define lowbit(i) (i&-i)
 3 using namespace std;
 4 int n,m,a[1000006],tree[1000006],book[1000006],ans[1000006],last;
 5 struct Query{
 6   int l,r,pos;
 7 }q[1000006];
 8
 9 bool cmp(Query a,Query b){
10   return a.r<b.r;
11 }
12
13 void add(int i,int a){
14   for(;i<=n;i+=lowbit(i))
15     tree[i]+=a;
16 }
17
18 int sum(int i){
19   int ret=0;
20   for(;i;i-=lowbit(i))
21     ret+=tree[i];
22   return ret;
23 }
24
25 int main(){
26   scanf("%d",&n);
27   for(int i=1;i<=n;++i)
28     scanf("%d",&a[i]);
29   scanf("%d",&m);
30   for(int i=1;i<=m;++i)
31     scanf("%d%d",&q[i].l,&q[i].r),q[i].pos=i;
32   sort(q+1,q+1+m,cmp);
33
34   int last=1;
35   for(int i=1;i<=m;++i){
36       for(int j=last;j<=q[i].r;++j){
37         if(book[a[j]])
38           add(book[a[j]],-1);
39         add(j,1);
40         book[a[j]]=j;
41     }
42     last=q[i].r+1;
43     ans[q[i].pos]=sum(q[i].r)-sum(q[i].l-1);
44   }
45   for(int i=1;i<=m;++i)
46     printf("%d\n",ans[i]);
47 } 

原文地址:https://www.cnblogs.com/vv123/p/12315269.html

时间: 2024-08-27 19:19:38

树状数组的原理和基础应用的相关文章

POJ - 2155 Matrix (二维树状数组 + 区间改动 + 单点求值 或者 二维线段树 + 区间更新 + 单点求值)

POJ - 2155 Matrix Time Limit: 3000MS   Memory Limit: 65536KB   64bit IO Format: %I64d & %I64u Submit Status Description Given an N*N matrix A, whose elements are either 0 or 1. A[i, j] means the number in the i-th row and j-th column. Initially we ha

HDU_2642_二维树状数组

Stars Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/65536 K (Java/Others)Total Submission(s): 1628    Accepted Submission(s): 683 Problem Description Yifenfei is a romantic guy and he likes to count the stars in the sky.To make the pr

Day2:T4求逆序对(树状数组+归并排序)

T4: 求逆序对 A[I]为前缀和 推导 (A[J]-A[I])/(J-I)>=M A[j]-A[I]>=M(J-I) A[J]-M*J>=A[I]-M*I 设B[]=A[]-M*(); B[J]>=B[I] 也就是求逆序对: 求逆序对的方法主要有两种: 归并排序: 树状数组: 这里两种方法都学习一下: 1.之前对于树状数组的印象就只有单点修改和区间求和 一直觉得lowbit是一个神奇的东西(至今没有搞懂原理) 上网搜了一下用树状数组求逆序对的方法,发现有一个大神写的很棒....看

HDU 3584 Cube(三维树状数组)

Cube Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others) Total Submission(s): 1660    Accepted Submission(s): 865 Problem Description Given an N*N*N cube A, whose elements are either 0 or 1. A[i, j, k] means the numbe

ST表与树状数组

ST表  st表可以解决区间最值的问题.可以做到O(nlogn)预处理 ,O(1)查询,但是不支持修改. st表的大概思路就是用st[i][j]来表示从i开始的2的j次方个树中的最值,查询时就从左端点开始,找到区间长度是2的多少次方,然后进行查询.然而,很明显,我们要查询的区间长度不一定是2的多少次幂.那怎么做到O(1)查询呢,这就要用到最值的特性. 如图,假如我们要查询2到7之间的最大值,但是7-2+1在22与23之间,我们选择22,也就是st[2][2],那剩下的6,7怎么办,我们考虑倒着从

树状数组review学习

学习参考: https://blog.csdn.net/flushhip/article/details/79165701 https://blog.csdn.net/moep0/article/details/52770728 树状数组在寒假集训的时候其实有讲过,但是当时只是有了板子,没有很深地去学习理解,现在回来想去理解一下这个很好用的数据结构. 树状数组的作用 树状数组是对于一个用来处理动态更新.动态统计区间问题的一种良好的数据结构,查询和修改复杂度都为O(logn)的数据结构. 树状数组

树状数组lowbit()函数原理的解释 x&amp;(x^(x-1)) x&amp;-x

树状数组lowbit()函数所求的就是最低位1的位置所以可以通过位运算来计算 树状数组通过 x&(x^(x-1)) 能够成功求出lowbit的原因: 首先设x=6,即110(2) 于是我们使 x-1=101 可以发现,当我们将一个二进制数减一时,从最低位一(即lowbit)开始向后的部分与之前全部相反,因为减去的1对后面的每一位都有影响,同时因为是二进制,影响就是让每一位都取反了 110 101 从最低位一(第二位)开始向后全部相反了 所以我们再与 x 异或一发,那么从lowbit开始往后全是1

树状数组(二叉索引树 BIT Fenwick树) *【一维基础模板】(查询区间和+修改更新)

刘汝佳:<训练指南>Page(194) #include <stdio.h> #include <string.h> #include <stdlib.h> #include <algorithm> using namespace std; //一维树状数组基础模板 int lowbit(int x) { return x&(-x); } int c[1001]; int sum(int x) //计算从1到x的数组元素的和 { int

算法基础-树状数组

今天我们分享一下树状数组,前置知识-了解树的结构,知道什么是左右儿子,各个节点的名称,也就是有点基础吧.今天以一个实际问题引出树状数组吧,中查询l-r的区间.(以B站大佬的课件为例子,可以关注下,在最后放上链接) 如果是暴力的话,显然时间复杂度是接受不了的(o(n方)),为了解决这个问题,我们就要用一些高级的数据结构.就是我们今天介绍的树状数组. 首先看下树状数组是什么, 树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度都为log