这两个东西没什么关系,纯属凑篇幅
差分约束系统就是给出一些形如x-y<=w不等式的约束,问你是否有满足问题的解,或者求最小,最大解。
这个问题的神奇之处是可以转化为图论的最短路问题。
所以我们需要用到的知识:
最短路径(主要是spfa)
================分割线================
其实看到不等式,我们也可以想起最短路中的一个叫松弛操作的东西:
对比上面的不等式,两个不等式的不等号正好相反,但是再仔细一想,其实它们的逻辑是一致的,因为SPFA的松弛操作是在满足小于的情况下进行松弛,所以我们可以将每个不等式转化成图上的有向边:
对于每个不等式 x[i]−x[j]<=a[k] x [ i ] − x [ j ] <= a [ k ] ,对结点 j j 和 i i 建立一条 j−>i j − > i 的有向边,边权为 a[k] a [ k ] ,求 x[n−1]−x[0] x [ n − 1 ] − x [ 0 ] 的最大值就是求 0 0 到 n−1 n − 1 的最短路。
当然,如果求最小值呢?看下面啦:
关于建图:
如果给出的不等式有”<=”也有”>=”,又该如何解决呢?
很明显,首先需要关注最后的问题是什么,如果需要求的是两个变量差的最大值,那么需要将所有不等式转变成”<=”的形式,建图后求最短路;相反,如果需要求的是两个变量差的最小值,那么需要将所有不等式转化成”>=”,建图后求最长路。
如果有形如:A - B = c 这样的等式呢?我们可以将它转化成以下两个不等式:
再通过上面的方法将其中一种不等号反向,建图即可。
最后,如果这些变量都是整数域上的,那么遇到A - B < c这样的不带等号的不等式,我们需要将它转化成”<=”或者”>=”的形式,即 A - B <= c - 1。
大概就是这样的了。
例题很多,比如最近我做的CCPC-FINAL里面就有一道。
一般来说比较裸吧。
==============分割线==================
关于可并堆,只简单学习了左偏树和斜堆。
fib堆看起来比较复杂,以后再系统学习吧。
学习资料可以看《左偏树的特点及其应用》
不想下载的看这个博客,博主是转载的。
===============分割线================
由于我不是很想写自己的理解,贴一下文章里的吧。
以下均为转载
[性质1] 节点的键值小于或等于它的左右子节点的键值。
即 key(i)≤key(parent(i)) k e y ( i ) ≤ k e y ( p a r e n t ( i ) ) 这条性质又叫堆性质。符合该性质的树是堆有序的 (Heap−Ordered) ( H e a p − O r d e r e d ) 。有了性质1,我们可以知道左偏树的根节点是整棵树的最小节点,于是我们可以在 O(1) O ( 1 ) 的时间内完成取最小节点操作。
[性质2] 节点的左子节点的距离不小于右子节点的距离。
即 dist(left(i))≥dist(right(i)) d i s t ( l e f t ( i ) ) ≥ d i s t ( r i g h t ( i ) ) 这条性质称为左偏性质。性质2是为了使我们可以以更小的代价在优先队列的其它两个基本操作(插入节点、删除最小节点)进行后维持堆性质。在后面我们就会看到它的作用。
这两条性质是对每一个节点而言的,因此可以简单地从中得出,左偏树的左右子树都是左偏树。
由这两条性质,我们可以得出左偏树的定义:左偏树是具有左偏性质的堆有序二叉树。
[性质3] 节点的距离等于它的右子节点的距离加1。
即 dist(i)=dist(right(i))+1 d i s t ( i ) = d i s t ( r i g h t ( i ) ) + 1 外节点的距离为0,由于性质2,它的右子节点必为空节点。为了满足性质3,故前面规定空节点的距离为-1。
我们的印象中,平衡树是具有非常小的深度的,这也意味着到达任何一个节点所经过的边数很少。左偏树并不是为了快速访问所有的节点而设计的,它的目的是快速访问最小节点以及在对树修改后快速的恢复堆性质。从图中我们可以看到它并不平衡,由于性质2的缘故,它的结构偏向左侧,不过距离的概念和树的深度并不同,左偏树并不意味着左子树的节点数或是深度一定大于右子树。
下面我们来讨论左偏树的距离和节点数的关系。
[引理1] 若左偏树的距离为一定值,则节点数最少的左偏树是完全二叉树。
证明:由性质2可知,当且仅当对于一棵左偏树中的每个节点i,都有 dist(left(i))=dist(right(i)) d i s t ( l e f t ( i ) ) = d i s t ( r i g h t ( i ) ) 时,该左偏树的节点数最少。显然具有这样性质的二叉树是完全二叉树。
[定理1] 若一棵左偏树的距离为k,则这棵左偏树至少有 2k+1−1 2 k + 1 − 1 个节点。
证明:由引理1可知,当这样的左偏树节点数最少的时候,是一棵完全二叉树。距离为k的完全二叉树高度也为k,节点数为 2k+1−1 2 k + 1 − 1 ,所以距离为k的左偏树至少有 2k+1−1 2 k + 1 − 1 个节点。
作为定理1的推论,我们有:
[性质4] 一棵N个节点的左偏树距离最多为 ëlog(N+1)û−1。 ë l o g ( N + 1 ) û − 1 。
证明:设一棵N个节点的左偏树距离为k,由定理1可知, N≥2k+1−1 N ≥ 2 k + 1 − 1 ,因此 k≤ëlog(N+1)û−1 k ≤ ë l o g ( N + 1 ) û − 1 。
C←Merge(A,B) C ← M e r g e ( A , B )
Merge()把A,B两棵左偏树合并,返回一棵新的左偏树C,包含A和B中的所有元素。在本文中,一棵左偏树用它的根节点的指针表示。 M e r g e ( ) 把 A , B 两 棵 左 偏 树 合 并 , 返 回 一 棵 新 的 左 偏 树 C , 包 含 A 和 B 中 的 所 有 元 素 。 在 本 文 中 , 一 棵 左 偏 树 用 它 的 根 节 点 的 指 针 表 示 。
在合并操作中,最简单的情况是其中一棵树为空(也就是,该树根节点指针为NULL)。 在 合 并 操 作 中 , 最 简 单 的 情 况 是 其 中 一 棵 树 为 空 ( 也 就 是 , 该 树 根 节 点 指 针 为 N U L L ) 。
这时我们只须要返回另一棵树。
若 A和B A 和 B 都非空,我们假设 A A 的根节点小于等于 B B 的根节点(否则交换 A,B A , B ),把 A A 的根节点作为新树 C C 的根节点,剩下的事就是合并A的右子树 right(A)和B r i g h t ( A ) 和 B 了。
right(A)←Merge(right(A),B) r i g h t ( A ) ← M e r g e ( r i g h t ( A ) , B )
合并了right(A)和B之后,right(A)的距离可能会变大,当right(A)的距离大于left(A)的距离时,左偏树的性质2会被破坏。在这种情况下,我们只须要交换left(A)和right(A)。 合 并 了 r i g h t ( A ) 和 B 之 后 , r i g h t ( A ) 的 距 离 可 能 会 变 大 , 当 r i g h t ( A ) 的 距 离 大 于 l e f t ( A ) 的 距 离 时 , 左 偏 树 的 性 质 2 会 被 破 坏 。 在 这 种 情 况 下 , 我 们 只 须 要 交 换 l e f t ( A ) 和 r i g h t ( A ) 。
若dist(left(A))>dist(right(A)),交换left(A)和right(A) 若 d i s t ( l e f t ( A ) ) > d i s t ( r i g h t ( A ) ) , 交 换 l e f t ( A ) 和 r i g h t ( A )
最后,由于 right(A) r i g h t ( A ) 的距离可能发生改变,我们必须更新A的距离:
dist(A)←dist(right(A))+1 d i s t ( A ) ← d i s t ( r i g h t ( A ) ) + 1
不难验证,经这样合并后的树C符合性质1和性质2,因此是一棵左偏树。至此左偏树的合并就完成了。
单节点的树一定是左偏树,因此向左偏树插入一个节点可以看作是对两棵左偏树的合并。下面是插入新节点的代码:
Procedure Insert(x, A)
B ← MakeIntoTree(x)
A ← Merge(A, B)
End Procedure
由于合并的其中一棵树只有一个节点,因此插入新节点操作的时间复杂度是 O(logn) O ( l o g n ) 。
由性质1,我们知道,左偏树的根节点是最小节点。在删除根节点后,剩下的两棵子树都是左偏树,需要把他们合并。删除最小节点操作的代码也非常简单:
Function DeleteMin(A)
t ← key(root(A))
A ← Merge(left(A), right(A))
return t
End Function
由于删除最小节点后只需进行一次合并,因此删除最小节点的时间复杂度也为 O(logn) O ( l o g n ) 。
将n个节点构建成一棵左偏树,这也是一个常用的操作。
【算法一 】 暴力算法——逐个节点插入,时间复杂度为 O(nlogn) O ( n l o g n ) 。
【算法二 】 仿照二叉堆的构建算法,我们可以得到下面这种算法:
时间复杂度 O(n) O ( n )
==============分割线=================
这是一个每次合并都交换左右孩子的算法。
合并大概就是:
总之个人感觉很玄学。
好fake的数据结构啊