高级数据结构-树状数组

基本知识:

1.lowbit运算

cpp 复制代码
int lowbit(int x){
	return x & -x;
}

2.树状数组及其应用

aN是原始数组;

cN是树状数组,存放数组a中i号位之前的lowbit(i)个元素之和,ci覆盖长度是lowbit(i)

特别强调 树状数组的下标必须从1开始!!!

(1).函数getSum(x),返回前x个数之和a1+a2+......ax.

cpp 复制代码
int getsum(int x){
	int res = 0;//记录和
	for(int i = x;i;i -= lowbit(i))
		res += tr[i];
	return res;
}

(2).函数update(x,c).实现将第x个数加上一个数v的功能,即ax += c;

cpp 复制代码
void update(int x,int c){
	for(int i = x;i <= n;i += lowbit(i)){
		tr[i] += c;
	}
}

例题1:

在完成了分配任务之后,西部 314来到了楼兰古城的西部。

相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀(V),一个部落崇拜铁锹(∧),他们分别用 V 和 ∧ 的形状来代表各自部落的图腾。

西部 314 在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了 n 个点,经测量发现这 n𝑛 个点的水平位置和竖直位置是两两不同的。

西部 314认为这幅壁画所包含的信息与这 n 个点的相对位置有关,因此不妨设坐标分别为 (1,y1),(2,y2),...,(n,yn),其中 y1∼yn是 11到 n 的一个排列。

西部 314314 打算研究这幅壁画中包含着多少个图腾。

如果三个点 (i,yi),(j,yj),(k,yk) 满足 1≤i<j<k≤n 且 yi>yj,yj<yk,则称这三个点构成 V 图腾;

如果三个点 (i,yi),(j,yj),(k,yk) 满足 1≤i<j<k≤n 且 yi<yj,yj>yk,则称这三个点构成 ∧ 图腾;

西部 314想知道,这 n个点中两个部落图腾的数目。

因此,你需要编写一个程序来求出 V 的个数和 ∧ 的个数。

输入格式

第一行一个数 n𝑛。

第二行是 n𝑛 个数,分别代表 y1,y2,...,yn。

输出格式

两个数,中间用空格隔开,依次为 V 的个数和 ∧ 的个数。

数据范围

对于所有数据,n≤200000,且输出答案不会超过𝑖𝑛𝑡64。

𝑦1∼𝑦𝑛 是 1 到 n 的一个排列。

输入样例:
复制代码
5
1 5 3 2 4
输出样例:
复制代码
3 4

代码:

cpp 复制代码
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<unordered_map>
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N = 2e5 + 10;
int n,a[N],tr[N],Greater[N],lower[N];
int lowbit(int x){
	return x & -x;
}
void add(int x,int c){
	for(int i = x;i <= n;i += lowbit(i))
		tr[i] += c;
}
int sum(int x){
	int res = 0;
	for(int i = x;i;i -= lowbit(i))
		res += tr[i];
	return res;
}
signed main(){
	cin >> n;
	for(int i = 1;i <= n;i ++){
		cin >> a[i];
	}
	for(int i = 1;i <= n;i ++){
		int y = a[i];
		Greater[i] = sum(n) - sum(y);
		lower[i] = sum(y-1);
		add(y,1);
	}
	memset(tr,0,sizeof tr);
	int res1 = 0,res2 = 0;
	for(int i = n;i;i --){
		int y = a[i];
		res1 += Greater[i]*(sum(n) - sum(y));
		res2 += lower[i]*(sum(y-1));
		add(y,1);
	}
	cout << res1 << ' ' << res2 ;
}

例题2:

给定一个长度为 𝑁 的数列 𝐴,以及 𝑀 条指令,每条指令可能是以下两种之一:

  1. C l r d,表示把 Al,Al+1,...,Ar都加上 𝑑。
  2. Q l r,表示询问数列中第𝑙∼𝑟 个数的和。

对于每个询问,输出一个整数表示答案。

输入格式

第一行两个整数 𝑁,𝑀。

第二行 𝑁 个整数 𝐴𝑖。

接下来 𝑀 行表示 𝑀 条指令,每条指令的格式如题目描述所示。

输出格式

对于每个询问,输出一个整数表示答案。

每个答案占一行。

数据范围

1≤𝑁,𝑀≤105,

|𝑑|≤10000,

|𝐴𝑖|≤109

输入样例:
复制代码
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
输出样例:
复制代码
4
55
9
15

代码:

cpp 复制代码
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<unordered_map>
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N = 1e5 + 10;
int n,m;
int a[N],tr[N];
int lowbit(int x){
	return x & -x;
}
void add(int x,int c){
	for(int i = x;i <= n;i += lowbit(i)){
		tr[i] += c;
	}
}
int sum(int x){
	int res = 0;//记录和
	for(int i = x;i;i -= lowbit(i))
		res += tr[i];
	return res;
}
signed main(){
	cin >> n >> m;
	for(int i = 1;i <= n;i ++)
		cin >> a[i];
	for(int i = 1;i <= n;i ++){
		add(i,a[i] - a[i-1]);
	}
	while(m --){
		char c;
		int l,r,d,x;
		cin >> c;
		if(c == 'C'){
			cin >> l >> r >> d;
			add(l,d);
			add(r+1,-d);
		}
		if(c == 'Q'){
			cin >> x;
			cout << sum(x) << endl;
		}
	}
	return 0;
}
相关推荐
silvamux_18 分钟前
微头条爆款体平台适配实测:把交付要求变成可计算的指标
java·线性代数·算法·决策树·矩阵
重生之小比特1 小时前
【算法拾光】前缀和|连续的子数组和 + 和相同的二元子数组
算法·leetcode·哈希算法
一去不复返的通信er1 小时前
力扣15:盛最多水的容器
算法·leetcode
Navigator_Z1 小时前
LeetCode //C - 1309. Decrypt String from Alphabet to Integer Mapping
c语言·算法·leetcode
潇湘秦1 小时前
SQL Server 2016+ 整数类型元数据扩宽(秒级变更)完整说明
数据库·算法
小白起 v1 小时前
02-数组与链表
数据结构·链表
Figo_Cheung2 小时前
Figo基于结构空间算法假设(SSAH)的生成式大模型优化范式与工程实现边界研究——提出适应AGI架构演化的自适应拓扑感知表征机制
算法·架构·agi
旖旎夜光3 小时前
LCR 170:交易逆序对的总数(归并排序) —— 题解
c++·学习·算法·leetcode·排序算法
微尘hjx11 小时前
【ByteTrack 04】多轨迹算法ByteTrack改进点
算法·yolo·目标检测·目标跟踪·bytetrack·ultralytics·车跟踪
路灯上の马陈12 小时前
二叉树的定义与性质
数据结构·二叉树·树结构·1024程序员节