首页 > 其他分享 >树状数组

树状数组

时间:2023-04-30 23:44:24浏览次数:29  
标签:前缀 树状 int lowbit 查询 数组 区间

树状数组, 可以高效地计算数列前缀和, 它的查询(求前缀和) 和更新(修改) 操作都可以在 O(logn) 的时间完成


tr[i] 存储以 i 为终点, 长度为 lowbit(i) 的区间

修改: for( int i = x ; i <= n ; i += lowbit(i) ) tr[i] += c

查询: for( int i = x ; i ; i -= lowbit(i) ) sum += tr[i]


//树状数组操作

//返回非负整数x在二进制表示下最低位1及其后面的0构成的数值
int lowbit (int x)
{
    return x&-x;
}

//将序列中第x个数加上k
void add (int x,int k)
{
    for(int i=x;i<=n;i+=lowbit(i))tr[i]+=k;
}

//查询序列前x个数的和
int ask (int x)
{
    int sum=0;
    for(int i=x;i;i-=lowbit(i))sum+=tr[i];
    return sum;
}

//O(n)的建树方法
void build ()
{
    for(int x=1;x<=n;x++)
    {
        tr[x]=a[x];
        for(int i=x-1;i>=x-lowbit(x)+1;i-=lowbit(i))
            tr[x]+=tr[i];
    }
}



树状数组的应用

① 点更新, 区间查询

② 区间更新, 点查询

使用差分, 维护差分数组 d[i] = a[i] - a[i-1]

区间更新变成了 [l,r] 两端 lr 的更新, 点查询也就变成了 [1,x] 的区间查询

③ 区间更新, 区间查询

使用差分, 维护差分数组 c[i] = a[i] - a[i-1]

区间更新变成了 [l,r] 两端 lr 的更新

对于求解一个 S = a[1,x] 的前缀和, 有:

S = a[1] + a[2] + \(\cdots\) + a[x]

\(\quad\) = c[1] + (c[1] + c[2]) + \(\cdots\) + (c[1] + c[2] + ... + c[x])

\(\quad\) = x*c[1] + (x-1)*c[2] + \(\cdots\) + 1*c[x]

\(\quad\) = (x+1) * (c[1] + c[2] + ... + c[x]) - (1*c[1] + 2*c[2] + ... + x*c[x])

因此可以使用另一个辅助数组 D 来维护 d[i] = i*c[i] 的前缀和


//区间更新,区间查询模板

int n;
int a[N],tr1[N],tr2[N];
//a[]存储原数组,b[i]=a[i]-a[i-1]为a的差分数组,tr1[i]维护b[i]的前缀和,tr2[i]维护i*b[i]的前缀和

int lowbit (int x)
{
    return x&-x;
}

void add (int tr[],int x,int c)
{
    for(int i=x;i<=n;i+=lowbit(i))tr[i]+=c;
}

int sum (int tr[],int x)
{
    int res=0;
    for(int i=x;i;i-=lowbit(i))res+=tr[i];
    return res;
}

int prefix_sum (int x)	//求a[1,x]的前缀和
{
    return sum(tr1,x)*(x+1)-sum(tr2,x);
}

int main()
{
    for(int i=1;i<=n;i++)
    {
        int b=a[i]-a[i-1];	//差分数组b[i]=a[i]-a[i-1]
        add(tr1,i,b);		//tr1[]维护b[i]的前缀和
        add(tr2,i,b*i);		//tr2[]维护i*b[i]的前缀和
    }
}


标签:前缀,树状,int,lowbit,查询,数组,区间
From: https://www.cnblogs.com/evilboy/p/17365999.html

相关文章

  • NOI / 1.8编程基础之多维数组
    11:图像旋转1.描述输入一个n行m列的黑白图像,将它顺时针旋转90度后输出。2.输入第一行包含两个整数n和m,表示图像包含像素点的行数和列数。1<=n<=100,1<=m<=100。接下来n行,每行m个整数,表示图像的每个像素点灰度。相邻两个整数之间用单个空格隔开,每个元素均在0~255之间3.......
  • 将数组清空
    给你一个包含若干互不相同整数的数组nums,你需要执行以下操作直到数组为空:如果数组中第一个元素是当前数组中的最小值则删除它否则,将第一个元素移动到数组的末尾请你返回需要多少个操作使nums为空1.逆向思维classSolution{public:longlongcountOperationsToEmpt......
  • 【剑指 Offer】 51. 数组中的逆序对
    【题目】在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。 示例1:输入:[7,5,6,4]输出:5 限制:0<=数组长度<=50000来源:力扣(LeetCode)链接:https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-du......
  • Java根据Integer数组(有null值)递归构造二叉树
    二叉树:publicclassTreeNode{intval;TreeNodeleft;TreeNoderight;TreeNode(){}TreeNode(intval){this.val=val;}TreeNode(intval,TreeNodeleft,TreeNoderight){this.val=val;this.l......
  • Java二维数组
    二维数组二维数组的应用场景:但我们需要把数据分组管理的时候,就需要用到二维数组二维数组初始化:1、静态初始化:格式:数据类型[][]数组名=new数据类型[][]{{元素1,元素2},{元素1,元素2}};eg:int[][]arr=newint[][]{{11,22},{33,44}}简化格式:数据类型[][]数组名={{元素1......
  • HashMap的数组长度为何必须是2的n次方
    扩容方便,数字位移计算方便效率高;计算元素下标使用的方式是key的hash&(数组length-1),由于length是2^n,转换成二进制后2^-1最低位就全部都是1,比如111,就相当于是数组长度的掩码,那么hash&111就可以将数组的每一位都覆盖,加入数组长度不是2^n,那么length-1低位不全是1,比如101,那么h......
  • 2023-04-29:一个序列的 宽度 定义为该序列中最大元素和最小元素的差值。 给你一个整数
    2023-04-29:一个序列的宽度定义为该序列中最大元素和最小元素的差值。给你一个整数数组nums,返回nums的所有非空子序列的宽度之和由于答案可能非常大,请返回对109+7取余后的结果。子序列定义为从一个数组里删除一些(或者不删除)元素,但不改变剩下元素的顺序得到的数组......
  • 2023-04-29:一个序列的 宽度 定义为该序列中最大元素和最小元素的差值。 给你一个整数
    2023-04-29:一个序列的宽度定义为该序列中最大元素和最小元素的差值。给你一个整数数组nums,返回nums的所有非空子序列的宽度之和由于答案可能非常大,请返回对109+7取余后的结果。子序列定义为从一个数组里删除一些(或者不删除)元素,但不改变剩下元素的顺序得到的数......
  • 长度最小的子数组--Python解法
    给定一个含有 n 个正整数的数组和一个正整数target。找出该数组中满足其和≥target的长度最小的连续子数组 [numsl,numsl+1,...,numsr-1,numsr],并返回其长度。如果不存在符合条件的子数组,返回0。defminSubArrayLen(self,s:int,nums:List[int])->int:......
  • 树状数组 好题整理
    树状数组好题整理[SDOI2009]HH的项链离线询问后,按右端点升序排序,考虑建立一个树状数组,只包含0/1,把含每种颜色的点中最靠右的位置打上1的标记,询问\([l,r]\)答案即为\(query_r-query_{l-1}\),可以证明,如果一个相同颜色的点的位置对答案有贡献,那么最靠右的位置也一定能......