各种数据结构以及七七八八的东西

堆(一般指二叉堆),实质就是一颗完全二叉树,用来维护单调性

堆可以实现插入新值,得到最值(直接取堆顶值),删除最值。

插入新值,从堆尾插入,不断比较 上浮;删除最值,就是将堆顶替换掉,可以用堆尾 替换,并不断比较 下沉,用树的深度的时间花销维护堆的单调性

感受一下维护堆的过程,可以用数组实现(一一对应),手写堆就很容易写

手写堆 code
inline void up(int u)
{
	if (u / 2 && a[u / 2] > a[u]) 
		swap(a[u], a[u / 2]), up(u / 2);
}

inline void down(int u)
{
	int v = u;
	if (u * 2 <= cnt && a[u * 2] < a[u]) 
		v = u * 2;
	if (u * 2 + 1 <= cnt && a[u * 2 + 1] < a[v]) 
		v = u * 2 + 1;
	if (v != u) 
		swap(a[u], a[v]), down(v);
}

inline void push(int x)
{
	a[++ cnt] = x;
	up(cnt);
}

inline void pop()
{
	a[1] = a[cnt]; 
	cnt --;
	down(1);
}

priority_queue 就是 STL 里封装好的堆(默认大根堆)

另外,由于这个用堆尾替换堆顶的等价操作(等价于删除堆顶操作),两个相等的元素可能位置会有变化,即后进堆的可能深度更浅一些,所以,堆排序是不稳定的

对于某一种排序算法,如果待排序的序列中存在值相等的元素,经过排序之后,相等元素之间原有的先后顺序保持不变,那么就说这个排序算法是稳定的。


这里我们从 rmq 问题开始,讨论 st表(有点简单且扩展差,略)、树状数组、线段树。。。甚至分块

先放个表格对比一下,一些基础操作

算法 预处理 单点查询 单点修改 区间修改
朴素算法 \(O(n)\) \(O(1)\) \(O(n)\)
st 表 \(O(n\log n)\) \(O(1)\) 不支持 不支持
树状数组 \(O(n)\) \(O(\log n)\) \(O(\log n)\) 结合差分可以做到 \(O(\log n)\)
线段树 \(O(n)\) \(O(\log n)\) \(O(\log n)\) \(O(\log n)\)
分块 \(O(n)\) \(O(\sqrt n)\) \(O(\sqrt n)\) \(O(\sqrt n)\)

树状数组

tip:lowbit(x) 的作用是返回 \((x)_2\) 从右往左第一个为 1 的数,即若 1 第一次出现的位置为 p,则返回 \(2^p\)

实现方法就是对 \((x)_{2}\) 先取反,再 + 1,得到 \((y)_2\) ,发现两二进制数在第一个 1 及右边的 0 是完全相同的,但是左边仍是对应取反的,此时可以 \(x \& y\) 得到结果

计算机中,刚好一个数的负数就是按照其补码 + 1 存储的,所以等价于 \(x\&(-x)\)

树状数组同样可以同时维护 区修 + 区查,

在差分思想的基础上,若查询原数组 \(a\)\([1,p]\) 的区间和,则有:

\[\begin{aligned}\sum\limits_{i=1}^{p}a[i] &= \sum\limits_{i=1}^{p}\sum\limits_{j=1}^{i}b[j] \\ &=\sum\limits_{i=1}^{p}[~b[i]\cdot(p-i+1)~] \\ &=\sum\limits_{i=1}^{p}[~b[i]\cdot(p+1)-b[i]\cdot i~] \\ &=\sum\limits_{i=1}^{p}[~b[i]\cdot(p+1)~]-\sum\limits_{i=1}^{p}(~b[i]\cdot i~) \end{aligned}\]

到最后一个式子就把不同的变量分离了,实现更简单,\(b[i]\cdot i\) 可以再建一个树状数组代替。

注意:树状数组中的下标不是原数组的下标,在 \(p\) 这个位置上加,所以是\(\times p\) ,而不是 \(\times x\)

code
int lowbit(int x)
{
	return x & (-x);
}
void add(int x, int k)
{
	int p = x;
	for (; x <= n; x += lowbit(x)) 
	{
		c1[x] += k;
		c2[x] += k * p;
	}
}
int query(int x)
{
	int ans = 0, p = x;
	for (; x ; x -= lowbit(x)) 
		ans += (p + 1) * c1[x] - c2[x];
	return ans;
}
void init()
{
	for (int i = 1; i <= n; i ++)
	{
		b[i] = a[i] - a[i-1];
		add(i, b[i]);
	}
}
void get_add(int x, int y, int k)
{
	add(x, k);
	add(y + 1, -k);
}
int get_query(int x, int y)
{
	return query(y) - query(x - 1);
}
posted @ 2024-09-17 22:21  Zhang_Wenjie  阅读(26)  评论(0)    收藏  举报