两个挺偏门(?)的算法。
笛卡尔树
定义
笛卡尔树是一个维护数列的数据结构,它满足两个性质:
- 中序遍历等于原序列,并且是二叉树(这就是$\rm BST$的性质)。
- 任意一个点的权值都要小于(或者大于)儿子的权值(这就是堆的性质)。
所以根据定义容易得到一个暴力的构造方法:每次暴力找到当前区间的最小值,然后左右两边递归处理作为左右子树。
这个做法复杂度是$O(n^2)$的。
线性构造
线性构造其实很简单,考虑增量法,用一个栈来维护从根节点一直走右儿子的这条链,每次在序列最后添加一个值:
- 如果它是全局最小值(即小于根节点),就把根节点接在它左儿子,然后暴力更新栈(清空栈,把当前点加进去)。
- 否则一路弹栈,直到栈顶比当前点小,把当前点接在栈顶右儿子下,弹掉的那条链接在当前点左儿子下。
每个点只会被弹栈入栈一次,复杂度$O(n)$。
正确性显然。
应用
我是真的没找到什么题。。就两三个的样子。
[POJ2201] Cartesian Tree
模板题,放下代码吧(头文件之类的东西太占地方就去掉了):
1 | struct info {int k,x,id;}a[maxn]; |
[BZOJ2616] SPOJ PERIODNI
考虑把小根笛卡尔树搞出来,每个点$x$看做是一个$(val_x-val_{fa})\times size_x$的矩形,那么所有点的总和就是题目给出的图形。
考虑$\rm dp$,设$f_{x,i}$表示$x$子树放了$i$个点的方案数,那么首先弄一个辅助数组$t_i=\sum_{j=0}^{i} f_{ls,j}\cdot f_{rs,i-j}$表示子树的方案。
那么转移考虑枚举$x$代表的矩形放了几个:
复杂度$O(nk^2)$。
代码没啥重要的东西就不放了
[uoj424]【集训队作业2018】count
多项式不想写,O(n)做法看不懂,所以咕咕咕咕咕
标准rmq
问题转化
标准$\rm rmq$是一种预处理$O(n)$,每次询问$O(1)$的$\rm rmq$做法。
我们先把序列建成笛卡尔树,比较容易的可以得知,两个点之间的最小值就是笛卡尔树上的$\rm LCA$的权值。
问题转化为如何$O(n)-O(1)$求$\rm LCA$。
这个做法非常巧妙,首先我们可以利用欧拉序把$\rm LCA$再转化成深度的$\rm rmq$问题,而这个序列有一个性质:$|a_i-a_{i-1}|=1$一定成立。
根据这个性质我们可以得到一个$O(n)-O(1)$的特殊序列的$\rm rmq$算法,这个算法好像也被叫做$\rm \pm 1 rmq$或者约束$\rm rmq$。
接下来将阐述约束$\rm rmq$的做法。
约束rmq
首先对序列分块,设块大小$B=\dfrac{\log_2 n}{2}$,为什么待会解释。
那么首先很容易解决整块之间的询问:直接上$O(n\log n)$的$\rm ST$表就好了,因为一共只有$\dfrac{n}{B}=\dfrac{2n}{\log_2 n}$个块,所以这部分复杂度为$O(n)$。
现在难点就在如何求不全的块的贡献,注意到现在我们还没用到$\rm \pm1$的特殊性质。
我们考虑一共有多少个不等价的块,我们说两个块等价当且仅当他们的大小关系相同,或者也可以表述为他们的差分数组不同:因为每次只有可能$\rm \pm 1$,所以说这两句话是完全等价的。
考虑一共有多少个本质不同的差分序列,容易发现个数为$2^{B-1}=2^{(\log _2 n)/2-1}= O(\sqrt n)$。
这就是为什么设块大小的时候要除以$2$,这么一来我们可以暴力预处理出$f_{s,i,j}$表示第$s$种块$[i,j]$的最小值位置在哪里,那么只需要一开始给每个块标号就好了,标号的复杂度是$O(n)$,预处理$f$的复杂度是$O(\sqrt n \log ^2 n)$。
所以总预处理的复杂度是$O(n)$,而询问显然可以根据上面的东西做到$O(1)$。
放个代码吧,由于我太懒了,所以只实现了$O(n)-O(1)$的$\rm LCA$。
并且说实话这东西没啥大用,可能是我实现有点慢,反正就只比带log的算法快了一倍,但是凭空多了$100$行代码。。。
以下这份代码可以通过洛谷LCA模板。
1 |
|