左偏树

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了左偏树相关的知识,希望对你有一定的参考价值。

左偏树(Leftist Tree)

由于生病了所以不得不回家几天,但也不能太颓。。

 

左偏树的blog网上一抓一大把。

左偏树无非就是一个堆的变种——

它并不一定是一棵完全二叉树,但是它依然能做到很优秀。

为什么要引进左偏树?因为二叉堆不能合并——或者说,合并的效率极其低下。

介绍一下左偏树的性质——

1.首先,它与二叉堆有一个共同性质:是二叉的对于每一个节点,其域值都比两个子节点大(或小),就相当于大根(小根)堆;

2.定义一棵左偏树中的外节点为左子树或右子树为空的节点。定义节点i的距离dis(i)为节点i到它的后代中最近的外节点所经过的边数。

左偏树满足如下的左偏性质:任意节点i的左子节点的距离不小于右子节点的距离。

那么,不难得出,一个节点的距离等价于以该节点为根的子树的最右路径的长度。

所以,我们可以得出一个重要结论:一棵n个节点的左偏树的距离不超过log2(n+1)-1。

 

我们再来讲讲相关操作——

1.合并;

合并操作是最重要,最基本的操作,其他基本操作都可以通过合并操作实现,这也是左偏树的核心。

合并操作都是沿着两棵待合并左偏树的最右路径递归进行的。

一棵n个节点的左偏树最右路径上最多有log2(n+1)个节点,因此,合并操作也是log2n级别的。

合并操作有几个注意事项:

要保证二叉堆的性质;

要保证左偏树的性质(即任意节点左子节点的距离不小于右子节点的距离);

还要更新所属关系(比如某些题目需要某个节点所在堆的堆顶元素)。

 

2.插入;

插入一个节点可以看作是合并原树和以新节点为根的新树,相当于做一个合并操作,也需要log2n的时间。

 

3.删除;

这里的删除一般是删除堆顶(不会无缘无故删除堆里面的某个元素的)。

删除一个节点可以看作是删除根节点,把原根节点的左右子树合并成新树,也相当于做一个合并操作,也需要log2n的时间。不过要注意更新左右子树的所属信息。

 

来一道题目 Monkey King:

 

题目描述

 

Once in a forest, there lived N aggressive monkeys. At the beginning, they each does things in its own way and none of them knows each other. But monkeys can‘t avoid quarrelling, and it only happens between two monkeys who does not know each other. And when it happens, both the two monkeys will invite the strongest friend of them, and duel. Of course, after the duel, the two monkeys and all of there friends knows each other, and the quarrel above will no longer happens between these monkeys even if they have ever conflicted.

 

Assume that every money has a strongness value, which will be reduced to only half of the original after a duel(that is, 10 will be reduced to 5 and 5 will be reduced to 2).

 

And we also assume that every monkey knows himself. That is, when he is the strongest one in all of his friends, he himself will go to duel.

 

一开始有n只孤独的猴子,然后他们要打m次架,每次打架呢,都会拉上自己朋友最牛叉的出来跟别人打,打完之后战斗力就会减半,每次打完架就会成为朋友(正所谓不打不相识o(∩_∩)o )。问每次打完架之后那俩猴子最牛叉的朋友战斗力还有多少,若朋友打架就输出-1.

 

输入输出格式

输入格式:

 

 

There are several test cases, and each case consists of two parts.

 

First part: The first line contains an integer N(N<=100,000), which indicates the number of monkeys. And then N lines follows. There is one number on each line, indicating the strongness value of ith monkey(<=32768).

 

Second part: The first line contains an integer M(M<=100,000), which indicates there are M conflicts happened. And then M lines follows, each line of which contains two integers x and y, indicating that there is a conflict between the Xth monkey and Yth.

 

有多组数据

 

 

输出格式:

 

 

For each of the conflict, output -1 if the two monkeys know each other, otherwise output the strength value of the strongest monkey among all of its friends after the duel.

 

 

 

输入输出样例

 

输入样例#1:
5
20
16
10
10
4
5
2 3
3 4
3 5
4 5
1 5
输出样例#1:
8
5
5
-1
10

 

说明

 

题目可能有多组数据

 

显然,我们可以将一个猴子集团当作一棵左偏树,然后写一个左偏树支持如下操作:

查找一个左偏树的最大值,删除一个左偏树的根节点,合并两棵左偏树,调整合并好的左偏树(包含在合并操作里面)。

code:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=100005;
 6 int n,fa[N];
 7 struct LT {
 8     int w,l,r,d;
 9     void cle() {l=r=d=0;}
10 }a[N];
11 inline int read() {
12     int x=0,f=1; char ch=getchar();
13     while (ch<0||ch>9) {if (ch==-) f=-f; ch=getchar();}
14     while (ch>=0&&ch<=9) x=x*10+ch-0,ch=getchar();
15     return x*f;
16 }
17 inline int get(int x) {
18     return fa[x]==x?x:get(fa[x]);
19 }
20 int merge(int x,int y) {
21     if (!x) return y; if (!y) return x;
22     if (a[x].w<a[y].w) swap(a[x],a[y]);
23     a[x].r=merge(a[x].r,y);
24     int l,r; l=a[x].l,r=a[x].r,fa[r]=x;
25     if (a[l].d<a[r].d) swap(a[x].l,a[x].r);
26     a[x].d=(a[x].r)?a[a[x].r].d+1:0;
27     return x;
28 }
29 int del(int x) {
30     int l,r; l=a[x].l,r=a[x].r;
31     a[x].cle(),fa[l]=l,fa[r]=r;
32     return merge(l,r);
33 }
34 int main() {
35     while (scanf("%d",&n)!=EOF) {
36         for (int i=1; i<=n; i++)
37             a[i].cle(),a[i].w=read(),fa[i]=i;
38         for (int Q=read(); Q; Q--) {
39             int x=read(),y=read();
40             x=get(x),y=get(y);
41             if (x==y) {printf("%d\n",-1); continue;}
42             a[x].w>>=1,a[y].w>>=1;
43             int l,r; l=del(x),r=del(y);
44             l=merge(l,x),r=merge(r,y),l=merge(l,r);
45             printf("%d\n",a[l].w);
46         }
47     }
48     return 0;
49 }

 

以上是关于左偏树的主要内容,如果未能解决你的问题,请参考以下文章

P3273 [SCOI2011]棘手的操作 左偏树

左偏树

左偏树

左偏树的关系

并不对劲的左偏树

luogu_P3377 左偏树(可并堆)