一,倍增
从字面的上意思看就是成倍的增长 ,这是指我们在进行递推时,如果状态空间很大,通常的线性递推无法满足时间和空间复杂度的要求 ,那么我们就可以通过成倍的增长,只递推状态空间中在 2 的整数次幂位置上的值作为代表 。
“倍增” 与 “二进制划分” 两个思想 互相结合,降低了求解很多问题的 时间 与 空间复杂度。
具体的问题有:
快速幂;求解RMQ(区间最值)问题;求解最近公共祖先(LCA)等在树上的倍增应用等
二,前缀和
前缀和是指某序列的前n项和,可以把它理解为数学上的数列的前n项和。
1.一维前缀和:
预处理O(n):S[i] = a[1]+...+a[i]
查询O(1):a[r]+...+a[l] = S[l] - S[r-1]
2.二维前缀和
预处理O(nm):S[i][j] = S[i-1][j] + S[i][j-1] + a[i][j] - S[i-1][j-1]
查询O(1):[x1:x2,y1:y2]的子矩阵的和 = S[x2][y2] - S[x1-1][y2] - S[x2][y1-1] + S[x1-1][y1-1]
三,差分
差分可以看成前缀和的逆运算,类似于数学中的求导和积分。
1.一维差分
给定一个原数组a
构造一个数组b
使得a[i] = b[1] + b[2] + ... + b[i],即a数组是b数组的前缀和数组,b数组是a数组的差分数组。
已知a数组的情况下:
预处理O(n):b[i] = a[i] - a[i-1]
例题:
给a数组[l,r]区间中的每个数加上c,只需要 b[l] + c,b[r+1] - c 即可。
b[l] + c 则a数组区间[l:]中的每个数都会+c
b[r+1] - c 则a数组区间[r+1:]中的每个数都会-c
使得仅有区间[l,r]中的数+c
倘若有m个区间,时间复杂度为O(2*n + m);若暴力求解,时间复杂度为O(n*m)
2.二维差分
二维数组a是数组b的前缀和数组,b是a的差分数组。
void insert(int x1,int y1,int x2,int y2,int c) { //对b数组执行插入操作,等价于对a数组中的(x1,y1)到(x2,y2)之间的元素都加上了c b[x1][y1] += c; b[x2 + 1][y1] -= c; b[x1][y2 + 1] -= c; b[x2 + 1][y2 + 1] += c; }
已知a数组求b数组:
方法一:
先假想a数组为空,那么b数组一开始也为空,但是实际上a数组并不为空,因此我们每次让以(i,j)为左上角到以(i,j)为右下角面积内元素(其实就是一个小方格的面积)去插入 c = a[i][j] ,等价于原数组a中(i,j) 到(i,j)范围内 加上了 a[i][j] ,因此执行 n*m次插入操作,就成功构建了差分b数组.
for(int i = 1;i <= n;i++) { for(int j = 1;j <= m;j++) { insert(i, j, i, j, a[i][j]); //构建差分数组 } }
方法二:
直接构造的方法
b[i][j] = a[i][j] - a[i-1][j] - a[i][j-1] + a[i-1][j-1]
四,离散化
把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。通俗的说,离散化是在不改变数据相对大小的条件下,对数据进行相应的缩小。
适用范围:数组中元素值域很大,但个数不是很多。
比如将a[]=[1,3,100,2000,500000]映射到[0,1,2,3,4]这个过程就叫离散化。离散化常与差分、前缀和、数组数组、线段树结合考查。
五,二分
二分题目主要分为二分查找、二分答案,二分类型分为整数二分、实数域上二分