这个东西在pb_ds中用过。但不知道是什么。然后刚好这几天有空就看了一下。发现其实
很简单。
他事实上就只是维护了一颗树而已。
我们假设现在有 N 个节点,每个节点都带有权值。我们要维护这些节点的一个堆。
假设我们要维护一个小根堆。
对于每个节点 i ,我们都有一个表 Son 维护他所有的儿子,并且还有一个值 Fai 表示他的父亲。
对于这颗树而言,我们保证每个节点的儿子的权值都要大于等于这个节点的权值。
那么最后这颗树的根,设为 Root ,就是权值最小的点。
接下来考虑堆的几个基本操作。
表示我们要把u,v这两个堆合并起来,接着返回这个新堆的根。
那么很显然的,我们只需要比较一下 Rootu 的权值和 Rootv 的权值,设权值大的是 y ,另外一个是 x ,接着直接把 Fay 设为 x ,把 y 加入到 Sonx 中即可。
最后返回 x 即可。
表示我们要往堆 u 中加入一个权值为 v 的节点。那么我们可以先新建一个堆,里面只有一个权值为 v 的点。然后把他们Merge在一起就好了。
表示我们要把节点 u 的权值减去 Δ,Δ≥0 .
这是一个在堆中非常常用的操作。
首先,若 u 就是他所处堆的根的话,这个堆没有任何变化。我们直接把权值修改了即可。
否则,我们先把 u 从这个堆中分离出来。
值得注意的是,我们不修改 Fau 的 Son 集合。我们只把 Fau 设为空。表示他现在是他所处堆的根。至于对 Fau 的影响,我们接下来会考虑。
把 u 分离出来后。我们直接修改他的权值。然后 Merge(原来的堆,u) 即可。
表示我们要把 u 这个堆的根给删掉。
这是整个 Paring_heap 中最复杂(其实也很简单),也是最神奇的地方。
我们知道按照我们上面的操作,我们的根的儿子个数最坏情况下是 O(N) 的。
设根为 Root
我们首先肯定要提取出 Root 的所有儿子。但注意的是,此时 Root 的 Son 表中可能有些并不是他的儿子。但事实上我们只需要判一下 Fav 是否等于 Root 即可。这是不影响时间复杂度的。而且这也简化了ChangeVal的编程复杂度。
假设现在的真正的儿子表就是 Son .
一种简单粗暴的方法就是枚举所有的儿子,然后把他们直接Merge在一起。
由于这个实在是太粗暴了。所以这个的时间复杂度是最差的。。
另外一种方法是,
回忆我们用最普通的堆来 O(N) 建出 N 个元素的堆的做法。
我们维护一个FIFO(first in first out)队列。
一开始先把所有的元素塞进这个队列里。
若当前队列中只有一个元素,那么我们的根就是这个元素。
否则我们取出队首的两个元素,设为 x,y ,接着再把 Merge(x,y) 塞入队列当中。
这种做法的单次时间复杂度是 O(儿子个数) 的。
但是我们发现这样子建出来的根的儿子个数肯定变少了。
很可惜我并不会分析这个时间复杂度。
我用pairing_heap打了个dijkstra,和priority_queue比较了一下。发现虽然算法一的理论复杂度是 O(NlogN+M) ,但是 Pop 的常数较大。同开了 O2 的priority_queue感觉差不多,还慢了一点,可能要松弛次数比 N 大得多才能体现出优势吧。
struct STACK
{
int A[MAXN],tot,cnt;
int Top()
{
if (!tot) return ++ cnt;
return A[tot --];
}
void Push(int x)
{
A[++ tot] = x;
}
};
struct Pairing_heap
{
STACK Edge,Point;
int Que[MAXN << 1];
LL Val[MAXN];
int Final[MAXN],Next[MAXN],Fa[MAXN],Refer[MAXN],Apear[MAXN],To[MAXN],Root;
int tmp;
int Merge(int a,int b)
{
if (Val[a] > Val[b]) {tmp = a,a = b,b = tmp;}
int v = Edge.Top();
To[v] = b,Next[v] = Final[a],Final[a] = v,Fa[b] = a;
return a;
}
void Insert(int x,int y)
{
int u = Point.Top();
Val[u] = y;
Refer[x] = u,Apear[u] = x;
if (Root) Root = Merge(u,Root); else Root = u;
}
void Extract(int x)
{
if (x == Root || !x) return;
Fa[x] = 0;
}
void ChangeVal(int x,int y)
{
x = Refer[x];
Extract(x);
Val[x] = y;
if (x != Root) Root = Merge(x,Root);
}
void Value(int x,int y)
{
if (!Refer[x]) Insert(x,y); else ChangeVal(x,y);
}
int Top()
{
return Apear[Root];
}
void Pop()
{
int fi = 0,en = 0;
for(int i = Final[Root];i;i = Next[i])
{
Edge.Push(i);
if (Fa[To[i]] == Root) Fa[To[i]] = 0,Que[++ en] = To[i];
}
Point.Push(Root);
Refer[Apear[Root]] = Final[Root] = Fa[Root] = 0;
Apear[Root] = 0;
Root = 0;
while (fi < en)
{
++ fi;
if (fi == en) {Root = Que[fi];return;}
int u = Que[fi],v = Que[++ fi];
Que[++ en] = Merge(u,v);
}
}
};