首页 > 其他分享 >洛谷题单指南-二叉堆与树状数组-P3374 【模板】树状数组 1

洛谷题单指南-二叉堆与树状数组-P3374 【模板】树状数组 1

时间:2024-11-18 11:21:20浏览次数:1  
标签:12 树状 int 洛谷题 tr 数组 lowbit

原题链接:https://www.luogu.com.cn/problem/P3374

题意解读:树状数组模版:单点修改,区间求值。

解题思路:

树状数组-Binary Index Tree可以动态维护一组数,可以O(logn)的修改一个数,也可以O(logn)的计算一段区间的和。

思考一下朴素做法:如何修改一个数,计算区间和?

如果是常规数组,修改操作是O(1),计算区间和需要先计算前缀和,复杂度为O(n)

如果是前缀和数组,修改操作是O(n),计算区间和是O(1)

如果数据变更m次,总的复杂度将达到m*n

而树状数组可以使得两种操作的复杂度都是O(logn)。

1、树状数组定义

原数组:a[i]表示第i个数,

树状数组:tr[i]表示从i往前lowbit(i)个数的和,

lowbit(i)的含义是取整数i的最后一个二进制1所代表的整数,如i = 12,对应二进制1100,lowbit(i) = 4,

在c++中lowbit(i)的计算可以用i & -i。

2、利用树状数组求区间和

设s[x]为a[1]~a[x]的前缀和,根据tr[]的定义可知:

int sum(int x)

{

  for(int i = x; i > 0; i -= lowbit(i)) s[x] += tr[i];

}

i减lowbit(i)最多持续logn次,所以计算前缀和的复杂度为O(logn)

有了前缀和,l~r的区间和就很容易求得:s[r] - s[l-1]

3、利用树状数组修改元数组的值

上图形式化表示a,tr的关系:

如tr[8]表示以a[8]往前lowbit(8)=8长度的a的元素之和,tr[12]表示以a[12]往前lowbit(12)=4长度的a的元素之和。

tr[16] = a[16] + tr[15] + tr[14] + tr[12] + tr[8]

tr[12] = a[12] + tr[11] 

tr[10] = a[10] + tr[9]

如此构成了一种树形关系,因此称为树状数组。

当要修改一个a元素的值a[x],关键问题在于要同时跟新与a[x]有关的若干个tr值

比如修改了a[11],显然在树中往根节点回溯即可找到所有受a[11]影响的tr值:tr[11]、tr[12]、tr[16]

11的二进制1011,12的二进制1100,16的二进制10000

它们的关系为:1011 + lowbit(1011) = 1100 ,1100 + lowbit(1100) = 10000

因此当修改一个a[x] += val,其所能影响的所有tr如下:

void add(int x, int val)

{

  for(int i = x; i <= n; i += lowbit(i)) tr[i] += val;

}

4、树状数组初始化

有两种初始化方法:

O(n * logn): 利用给元素增加值的函数,对于每一个a[i],都add(i, a[i])

O(n): 先求a的前缀和s,对于tr[i] = s[i] - s[i - lowbit(i) + 1]

100分代码:

 

#include <bits/stdc++.h>
using namespace std;

const int N = 500005;
int n, m;
int tr[N];

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

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

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

int main()
{
    cin >> n >> m;
    int a;
    for(int i = 1; i <= n; i++) 
    {
        cin >> a;
        add(i, a);
    }
    int op, x, y;
    while(m--)
    {
        cin >> op >> x >> y;
        if(op == 1) add(x, y);
        else if(op == 2) cout << sum(y) - sum(x - 1) << endl;
    }
    return 0;
}

 

标签:12,树状,int,洛谷题,tr,数组,lowbit
From: https://www.cnblogs.com/jcwy/p/18543218

相关文章

  • 动态规划 —— 子数组系列-最长湍流子数组
    1. 最长湍流子数组题目链接:978.最长湍流子数组-力扣(LeetCode)https://leetcode.cn/problems/longest-turbulent-subarray/description/ 2.题目解析假如有一个数组{a,b,c,d}如果在a这个位置,b比a大,呈上升趋势,c比b小,呈下降趋势,d比c大,呈上升趋势,像这种就是湍......
  • 最大子数组和
    最大子数组和题目给你一个整数数组nums,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组是数组中的一个连续部分。示例示例1:输入:nums=[-2,1,-3,4,-1,2,1,-5,4]输出:6解释:连续子数组[4,-1,2,1]的和最大,为6。示例2:输入:nums=......
  • 数组
    下面有关数组的叙述中,只有()是正确的:A)虽然不能对数组进行整体的赋值和IO等,但可以将整个数组作为参数传递给函数,函数也可以返回整个数组!B)C要求形参和相对应的实参都必须是类型相同的数组。C)形参数组和实参数组为同一数组,共同拥有一段内存空间。D)C语言规定数组名就是数......
  • 941. 有效的山脉数组
    题目自己写的classSolution{public:boolvalidMountainArray(vector<int>&arr){intl=0,r=1;boolup=true,change=false;if(arr.size()<3)returnfalse;if(arr[r]<arr[l])......
  • 代码随想录:长度最小的子数组
    代码随想录:长度最小的子数组现在不像考研那时候,每天时间都是固定的,以后可能还是以周为单位定目标比较好一点滑动窗口问题,之后记得和计算机网络里的滑动窗口对比,并且和背包问题对比classSolution{public:intminSubArrayLen(inttarget,vector<int>&nums){i......
  • STL之动态数组
    一、标准模板库(StandardTemplateLibrary,STL)是HP公司开发的一个C++模板库,包含一些常用的数据结构和算法。具有以下的组件:1.容器:容纳包含一组元素的对象。2.迭代器:提供访问容器的方法3.函数对象4.算法二、STL之向量——vector   vector是c++标准库提供的一个变长数......
  • 【C++笔记】一维数组元素处理
    目录1.插入元素方法代码2.删除元素方法代码3.交换元素方法代码1.插入元素方法概念:插入元素是指在数组的某个位置添加一个新元素,并将原来的元素向后移动。例如,将5插入到数组[1,2,4,6]的第二个位置,结果变为[1,5,2,4,6]。关键点:确定插入位置:首先要明......
  • 一文搞懂!数组作为函数输入如何声明?
    一维数组函数形参定义:voidarray_print(inta[])一维数组指针函数形参定义:voidarray_print(int*a)二维数组函数形参定义://必须指明数组的列数,数组的行数没有太大关系//因为函数调用时传递的是一个指针,它指向由行向量构成的一维数组//所以以下两种声明方式都可以......
  • (nice!!!)(LeetCode) 3240. 最少翻转次数使二进制矩阵回文 II (分类讨论、数组)
    题目:3240.最少翻转次数使二进制矩阵回文II思路:分类讨论,需要对行和列的个数进行讨论,时间复杂度为0(nm),细节看注释。C++版本:classSolution{public:intminFlips(vector<vector<int>>&grid){intans=0;intn=grid.size(),m=grid[0].size();......
  • 除自身以外数组的乘积
    力扣链接:.-力扣(LeetCode)给你一个整数数组 nums,返回数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在  32位 整数范围内。请 不要使用除法,且在 O(n) ......