1. 二分法适用范围
二分法适用在一个严格单调序列中找到给定的某个数。
2. 二分法模板提要
首先,在
1 | //二分区间位左闭右闭的[left, right],初值必须能覆盖解的所有可能取值 |
二分法适用在一个严格单调序列中找到给定的某个数。
首先,在
1 | //二分区间位左闭右闭的[left, right],初值必须能覆盖解的所有可能取值 |
给定一个1~n的排列a0,a1,⋯,an−1,求这个数列中的逆序数对。
我们可以把一个大的数列A分成左右两个子数列B、C,那么数列A中所有的逆序对必定来自于以下三者其一:
(1) i,j都属于左子数列B的逆序对(i,j);
(2) i,j都属于右子数列C的逆序对(i,j);
(3) i属于B而j属于C
对于这(1)和(2),可以通过递归求得,对于(3),我们可以对数列C中的每个数字,统计数列B中比它大的数字的个数,再把结果加起来就好。代码如下,复杂度为$O(n\log n)。
1 | typedef long long ll; |
滑动最小值
给定一个长度为n的数列a0,a1,⋯,an−1和一个整数k。求数列$bi = \min{a_i,a{i+1},\cdots,a_{i+k-1} }(i=0,1,\cdots,n-k)。
限制条件:
1≤k≤n≤106, 0≤ai≤109
使用双端队列可以在O(n)时间内解决这个问题。最开始时双端队列为空,然后不断维护双端队列使它按照下面的顺序,存储用于计算后面的最小值a的元素的下标。
设双端队列从头部开始的元素的值为xi,则xi<xi+1且ai<ai+11 | // 输入 |
POJ 2559: Largest Rectangle in a Histogram
给定从左到右n个矩形,已知这此矩形的宽度都为1,长度不完全相等,为h1,h2,⋯,hn。这些矩形相连排成一排,求在这些矩形包括的范围内能得到的面积最大的矩形,打印出该面积。所求矩形可以横跨多个矩形,但不能超出原有矩形所确定的范围。
1≤n≤100000
输入
7 2 1 4 5 1 3 3
输出
8
求解最大流的Ford-Fulkerson算法的邻接表实现的例子如下:
1 | #include<iostream> |
记最大流的流量为F,那么该算法最多进行F次深度优先搜索,所以其复杂度为O(F|E|)。不过,这是一个很松的上界,达到这种最坏复杂度的情况几乎不存在。
树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构。给一个初始值全为0的数列,a1,a2,⋯,an,树状数组可以进行如下操作:
线段树是一种二叉搜索树,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点(并非完全二叉树!!!)。实际应用时一般还要开4N的数组以免越界。
线段树的构造代码如下:
1 | #include <iostream> |
给出N个数字:a0,a1,⋯,an−1,每个数字不大于10000,给出一个整数S,在N个数字中挑选出连续子序列,使这个子序列和大于或等于S。请问这个连续的子序列长度的最小值。
我们设以as开始总和最初大于S时的连续子序列为as+⋯+at−1,这时
as+1+⋯+at−2<as+⋯+at−2<S所以从as+1开始总和最初超过S的连续子序列如果是as+1+⋯+at′−1的话,必然有t≤t′。利用这一性质便可以设计如下算法:
(1) 以s=t=sum=0初始化。
(2) 只要依然有sum<S,就不断将sum增加at,并将t增加1.
(3) 如果(2)中无法满足sum≥S则终止。否则的话,更新res=min(res,t-s)。
(4) 将sum减去as,s增加1然后回到(2)。
对于这个算法,因为t最多变化n次,因此只需O(n)的复杂度就可以求解这个问题了。
1 | #include<iostream> |
埃氏筛法
素数的个数:
给定整数n,请问n以内有多少个素数?
首先将2到n范围内的所有整数写下来,其中最小的数字2是素数,将表中所有2的倍数都划去,表中剩余的最小数字是3,它不能被更小的数整除,所以是素数,再将所有3的倍数划去。以此类推:
1 | int prime[MAX_N]; //第i个素数 |
埃氏筛法的复杂度仅有O(nloglogn)。