Count on a Treap
题目来源
Codechef Feb 2014 COT5
https://www.codechef.com/problems/COT5
问题提出
什么是Treap
- 是一颗二叉搜索树,每个节点拥有 k e y key key属性.
- 是一颗堆,每个节点拥有 w e i g h t weight weight属性
问题
-
n
n
n次操作,三种类型,要求维护"大根Treap"
- ( 0 , k , w ) (0,k,w) (0,k,w) ,插入 k e y key key为 k , w e i g h t k,weight k,weight为 w w w的点.
- ( 1 , k ) (1,k) (1,k),删除 k e y key key为 k k k的点
- ( 2 , k u , k v ) (2,k_u,k_v) (2,ku,kv),询问 k e y key key为 k u , k v k_u,k_v ku,kv的两点在 T r e a p Treap Treap中的距离.
保证 k e y key key, w e i g h t weight weight均不相同.
问题解答
d i s t ( u , v ) = d e p ( u ) + d e p ( v ) − 2 ∗ d e p ( l c a ) dist(u,v) = dep(u) + dep(v) - 2* dep(lca) dist(u,v)=dep(u)+dep(v)−2∗dep(lca)
我们以 k e y key key为下标,将这个树按中序遍历展开.
显然 [ k u , k v ] [k_u,k_v] [ku,kv]之间最大的 w e i g h t weight weight最大的点就是两点的 l c a lca lca.
证明:首先 l c a lca lca一定在区间 [ k u , k v ] [k_u,k_v] [ku,kv]之间,不然 k u , k v k_u,k_v ku,kv将位于 l c a lca lca的同一侧,这不可能.其次权重最大的点一定是这颗子树的根节点,这个点一定是祖先,如果它不是 l c a lca lca的话,那么它的 k e y key key将大于或小于 l c a lca lca形成的子树中所有的点的 k e y key key,也就是说它不在区间 [ k u , k v ] [k_u,k_v] [ku,kv]之间,矛盾.
至此,用线段树可以轻松维护 l c a lca lca.
那么如何维护一个点在树上的深度呢?
根据Treap的特殊性质,我们知道每个点的 w e i g h t weight weight一定小于他父亲的 w e i g h t weight weight.
从这个点到根节点的祖先链上的 w e i g h t weight weight是不断递增的.
这里有一个非常特殊的性质,在序列上就是从这个点往两侧各找一个直接的递增子序列,那么序列上的点都是他的祖先.
这个性质基于这样一个事实:每个点左右两侧的第一个大于它 w e i g h t weight weight的点,都是这个点的一个祖先.因为其中一个点是它的父亲,而另一个点…自己画图观察一下就好了.
因此两个递增序列的长度和就是它到根的距离.
如何维护某一个点向右的直接递增序列呢.也是使用线段树,参考我的另一篇博客,楼房重建.