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

树状数组

时间:2024-03-14 23:24:37浏览次数:17  
标签:树状 int 差分 数组 include define

树状数组简介

树状数组维护信息的类型:

树状数组一般用来维护可差分的信息

比如:累加和、累加乘或者出题人出了某个可差分的信息

不可差分的信息:比如最大值、最小值

不可差分的信息一般不用树状数组来维护,而会选择线段树来维护,因为线段树维护的思考难度低

大多数情况下,线段树可以代替树状数组,两者的时间复杂度差不多,单次调用都是O(log N)

线段树的优势:用法全面、思考难度低、维护信息类型多(包括可差分信息、不可差分信息)

线段树的劣势:代码较多、空间使用较大、常数时间较差

树状数组的优势:代码少、使用空间少、常数时间优异

树状数组的劣势:维护信息的类型少、维护某些不可差分的信息时思考难度大并且不易实现

一维数组上实现:单点增加、范围查询的树状数组

洛谷测试链接

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <stdbool.h>
#define u unsigned
#define ll long long
#define sc scanf
#define pr printf 
#define fr(i, j, n) for (int i = j; i < n; i++)
#define N 100001

int tree[5 * N];//树状数组 
int n;//原始数组的元素个数 
int m;//操作次数 
//暂时存放数值的变量 
int t;
int a;
int b;

int lowbit(int x);//取出x的最右侧的一对应的数 
void add(int i, int v);//在i位置增加v 
int sum(int r);//返回[1, r]区间的累加和 
int range(int l, int r);//返回[l, r]区间的累加和 

int main(int argc, char* argv[])
{
	sc("%d%d", &n, &m);//读入元素个数和操作个数 
	
	for (int i = 1; i <= n; i ++) {//构成一个树状数组 
		sc("%d", &t);
		add(i, t);//假设树状数组刚开始都是0,现在开始添加数 
	}
	
	for (int i = 1; i <= m; i ++) {
		sc("%d%d%d", &t, &a, &b);//读入三个数 
		
		if (t == 1) {//如果t是1,把对应的位置的数加b 
			add(a, b);
		}
		else {//t是2,打印[a, b]区间的累加和	 
			pr("%d\n", range(a, b));
		}
	}
	
	return 0;
}

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

void add(int i, int v)
{
	while(i <= n) {//下标不能够超过元素个数 
		tree[i] += v;
		i += lowbit(i);//每次加lowbit去下一个包含i位置的区间 
	}
}

int sum(int r)
{
	int ans = 0;
	
	while(r) {
		ans += tree[r];
		r -= lowbit(r);//每次减lowbit去下一个包含[1, r]的区间 
	}
	
	return ans;
}

int range(int l, int r)
{
	return sum(r) - sum(l - 1);//前缀和求区间问题 
}

一维数组上实现:区间增加、单点查询的树状数组

洛谷测试链接

利用差分数组的性质来实现区间增加和单点查询,本质还是树状数组的操作但维护的信息不一样

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <stdbool.h>
#define u unsigned
#define ll long long
#define sc scanf
#define pr printf 
#define fr(i, j, n) for (int i = j; i < n; i++)
#define N 100001

//树状数组维护差分信息 
int tree[N * 5] = {0};
int n;//原始数组的元素个数 
int m;//操作的次数 
int pre;//前一个元素默认为0 
//暂时存放的树的变量 
int t; 
int a[3];

int lowbit(int x);//返回x最右边的1对应的十进制 
void add(int i, int v);//在树状数组i位置的值增加v 
int sum(int r);//返回[1, r]范围的累加和 

int main(int argc, char* argv[])
{
	sc("%d%d", &n, &m);//读取数组的元素个数和操作个数 
	
	//读入数组的值 
	for (int i = 1; i <= n; i ++) {
		sc("%d", &t);
		
		add(i, t - pre);//把差分数组放入树状数组,树状数组来维护差分信息 
		pre = t;//存储前面的元素值,不然无法得到差分数组 
	}
	
	for (int i = 1; i <= m; i ++) {
		sc("%d", &t);//读入操作的类型 
		
		if (t == 1) {//给区间中的所有树增加一个值 
			sc("%d%d%d", a, a + 1, a + 2);
			//差分的操作 
			add(a[0], a[2]);
			add(a[1] + 1, -a[2]);
		}
		else {//单点查询 
			sc("%d", &a[0]);
			pr("%d\n", sum(a[0]));//差分数组的性质 
		}
	}
	
	return 0;
}

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

void add(int i, int v)
{
	while(i <= n) {
		tree[i] += v;
		i += lowbit(i);
	}
}

int sum(int r)
{
	int ans = 0;
	
	while(r) {
		ans += tree[r];
		r -= lowbit(r);
	}
	
	return ans;
}

 

标签:树状,int,差分,数组,include,define
From: https://www.cnblogs.com/lwj1239/p/18074006

相关文章

  • 一维数组_校门外的树
    任务描述某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米。我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置;数轴上的每个整数点,即0,1,2,……,L,都种有一棵树。由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表......
  • LeetCode题练习与总结:搜索旋转排序数组
    一、题目整数数组nums按升序排列,数组中的值互不相同。在传递给函数之前,nums在预先未知的某个下标k(0<=k<nums.length)上进行了旋转,使数组变为[nums[k],nums[k+1],...,nums[n-1],nums[0],nums[1],...,nums[k-1]](下标从0开始计数)。例如,[0,1,2,4,5,6,7]......
  • 数组练习-小习题
    多个字符从两端开始移动,向中间汇聚输出,例如:打印Hello,word!第一个打印出来H(左一),然后打印!(右一),接着e(右二),下面是d(左二).......依次打印,这里介绍一个关键字:strlen(),用来测量字符串的长度,注意字符串如果带有"\0",strlen是不计算\0的,只计算\0之前的字符数。system(“cls”):清理屏幕。#i......
  • LeetCode题练习与总结:在排序数组中查找元素的第一个和最后一个位置
    一、题目给你一个按照非递减顺序排列的整数数组nums,和一个目标值target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值target,返回 [-1,-1]。你必须设计并实现时间复杂度为 O(logn) 的算法解决此问题。二、解题思路1.查找起始位置:使......
  • lc2035 将数组分成两个数组并最小化数组和的差
    给你一个长度为2n的整数数组,需要将它分成两个长度为n的数组,分别求出两个数组的和,并最小化两个数组和之差的绝对值。nums中每个元素都需要放入两个数组之一,求最小的数组和之差。1<=n<=15;-1E7<=nums[i]<=1E7直接暴搜的话最坏时间复杂度是O(2^30),会TLE,可以使用双向dfs优化,每次df......
  • day-19 合并后数组中的最大元素
    思路:从后向前遍历数组,用tans记录每一种可能的最大值,ans为实际最大值。注意:若ans==0,返回nums[0]要用longcodeclassSolution{publiclongmaxArrayValue(int[]nums){longans=0;longtans=0;booleanflag=true;for(in......
  • C++动态数组
    #include<iostream>usingnamespacestd;intmain(){ intt,i=0,j=0; cin>>t; char*pc=nullptr;//初始化 int*pi=nullptr;//初始化 float*pf=nullptr;//初始化 intsum=0; intFLAG=0; while(FLAG<t) { charch; cin>>......
  • 【你也能从零基础学会网站开发】Web建站之javascript入门篇 Array数组
    ......
  • 数组常见操作【最大/最小/数据反转操作】
    importjava.util.Scanner;publicclassday_4_5{publicstaticvoidmain(String[]args){/*数组的常见操作*///遍历int[]arr={3,5,2,1,4};intmax=arr[0];intmax1=0;intmax2=0;for(in......
  • [牛客]小红的数组分配
    题目思路去考虑sort排序为相同数字为偶数个,输出格式错误的去思考了数组为pair代码#include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;constintN=1e6+10;inti,j,k,n,m,t,res,a[N]={0};strings;voidslove(){ cin>>n; for(i=0;i<n*2;i++)ci......