笛卡尔树学习笔记
这玩意就是把treap里面rand出来的那个东西换成你需要的键值的一种数据结构。
准确来说,treap是一种特殊的笛卡尔树。
假设你有许多键值\(<k,w>\),让其中一个满足堆性质,另一个满足二叉搜索树性质,就是笛卡尔树。
如果所有的k,w都不相同,那么笛卡尔树是唯一的。
至于栈构建啥的,手模一下估计就能理解。
放一个本人模板(我比较懒,只看的惯临接表存边,所以比较繁琐)
for( R i = 1 ; i <= n ; i ++ ){
a [i] = read() , k = top ;
while( k && a [st [k]] < a [i] ) k -- ;
if( k ) rs [st [k]] = i ;
if( k < top ) ls [i] = st [k + 1] ;
st [++ k] = i , top = k ;
} for( R i = 1 ; i <= n ; i ++ ){
if( ls [i] ) assert( !fa [ls [i]] ) , fa [ls [i]] = i , add( i , ls [i] ) ;
if( rs [i] ) assert( !fa [rs [i]] ) , fa [rs [i]] = i , add( i , rs [i] ) ;
} for( R i = 1 ; i <= n ; i ++ ) if( !fa [i] ) assert( !root ) , root = i ;
用笛卡尔树主要就是利用他一个性质:在堆意义下的一个值管辖的区间是连续的。
然后这个东西大概有一下三种用处
杂七杂八的用处
- 直接利用性质 :见oiwiki 上的例题。
- O(N+Q)解决rmq问题,利用了序列上\([l,r]\)内的最值就是\(LCA(a[l],a[r])\),需要配合tarjan离线求lca
笛卡尔树上分治
这个应该是比较常见的用途,一般和最值有关的问题都可以想一想笛卡尔树上分治。
HDU 6701
首先是一个区间的max,可以想笛卡尔树上分治。
然后发现比较棘手的问题在于不能有重复的数字。
所以需要利用一下启发式合并的思想。
对于一个笛卡尔树上的节点,找到他左边和右边较小的区间。
然后枚举较小的区间内的每个元素,看他不重复在另一个区间能延伸到哪里,直接统计就行了。
LuoGu 4755
其实和上题思路基本一样,还是对于一个最大值枚举较小的区间。
直接去另一个区间里面查小于等于一个的个数,直接主席树就好了。
(貌似还能离散化+二分+树状数组,不过没有主席树好想)
51nod 1934
这玩意貌似并不是笛卡尔树,只是一个组合数学吧。
记录下来每个区间最大值对应的pos,然后确定一个最大值pos之后
主要就是剩下的可以随便选放在一边,因为不会相互影响,看出来这个基本就梅了。
ICPC HK 2016
其实是一个贪心,但是不太好想,第一步需要把它考虑成拆手脚架。
然后发现如果拆最高的时候不把最高的拆掉,以后就拆不掉了,所以是有单调性的。
由于每次必须拆完,还能剩下一些次数,所以分别计下来就行了。
所以建一个笛卡尔树,记录把x的字数拆成和父亲一样高需要的次数。
那么答案就是左边次数+右边次数+把这个子树拆成父亲的次数。
从低到高更新就行了。
\(Summary\):笛卡尔树上分治的主要特点就是要看出来左右两边相互不影响(被最值划分)
笛卡尔树上dp
LuoGu 6453
很经典的笛卡尔树上dp。
首先需要明确,一个\(n*m\)的矩形上面放 \(k\)个车的方案是 \(C(n,k)*C(m,k)*k!\)
然后,不难发现对于一个点的左子树和右子树中高于父亲节点的部分相互独立。
所以可以根据这个来树上背包。
设计\(f_{i,j}\)代表笛卡尔树上\(i\)的子树内放\(j\)个车的方案数,前提是和父亲共有的部分不算他的地盘。
先合并子树,设为\(t_{j}\),则有
模拟35 排列(自己搜,不link了)
还有几道题,本人太弱暂时还不会(咕咕咕,不过貌似是好题)
可能是别人的内部题,不过有题面
https://blog.csdn.net/jokerwyt/article/details/77841124
https://blog.csdn.net/Eric1561759334/article/details/109059526

浙公网安备 33010602011771号