hdu 1512 Monkey King 左偏树(可并堆)

来源:互联网 发布:sopcast网络电视成人台 编辑:程序博客网 时间:2024/06/14 01:45

Monkey King

Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 6163 Accepted Submission(s): 2626

Problem Description
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.

Input
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.

Output
For each of the conflict, output -1 if the two monkeys know each other, otherwise output the strongness value of the strongest monkey in all friends of them after the duel.

Sample Input
5
20
16
10
10
4
5
2 3
3 4
3 5
4 5
1 5

Sample Output
8
5
5
-1
10

题意:有n只猴子,每只猴子有厉害值,一开始素不相识。
两只不熟的猴子相遇,它们会发生争执。然后,它们会邀请它们认识的最厉害的猴子决斗。决斗完这两只决斗的猴子的厉害值都会减半。决斗能促进友谊,这样这两拨素不相识的猴子就都认识了对方。
如果一只猴子不认识任何其他猴子,那么它就会亲自上阵。
每次给出两只猴子x,y,判断它们是否认识对方。若不认识,输出决斗后它们共同所在猴子群体中最厉害猴子的厉害值。
n,m≤100000
厉害值≤32768

【发现自己好久没有写过走心的博客了】
分析:
论文:关于左偏树
PPT:左偏树的PPT
不难想到并查集来判断两只猴子是否是认识的,但如何找出它认识的猴子中力量值最大的那个呢?堆可以做到。两只猴子打架后堆需要合并,可以用左偏树来实现。

左偏树是一种可并堆的实现。
左偏树是一棵二叉树,它的节点除了和二叉树的节点一样具有左右子树外,还有两个属性:键值和距离。
键值可以用来比较节点的大小。
距离是节点i到它的后代中,最近的外节点所经过的边数。

左偏树的性质:
1.节点键值不大于左右子节点的键值
2.左子节点距离不小于右子节点距离
3.左右子节点都是左偏树
4.节点的距离等于它的右子节点的距离加1

这道题可以看做是可并堆的例题了。具体操作见代码:
(代码有抄袭借鉴其他大佬)

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;const int N = 1000010;int n,m;int a[N];int fa[N];struct node{    int l,r;    int dis;    int strong;}tree[N];int find(int x){    if(fa[x]==x) return fa[x];    return fa[x]=find(fa[x]);}int merge(int x,int y){    if(!x) return y;    if(!y) return x;    if(tree[x].strong<tree[y].strong) swap(x,y);//保证左子树的键能最大     tree[x].r=merge(tree[x].r,y);//合并右子树     int l=tree[x].l,r=tree[x].r;    fa[r]=x;//更新右子树的根    if(tree[l].dis<tree[r].dis) swap(tree[x].l,tree[x].r);    //维护堆的性质 交换左右子树     if(tree[x].r==0) tree[x].dis=0;//叶子节点     else tree[x].dis=tree[tree[x].r].dis+1;//左偏树的性质     return x;}//合并两棵树 int del(int x){//删除根节点     int l=tree[x].l,r=tree[x].r;    fa[l]=l,fa[r]=r;    tree[x].l=tree[x].r=tree[x].dis=0;    return merge(l,r); }void solve(int x,int y){    tree[x].strong=tree[x].strong>>1;    tree[y].strong=tree[y].strong>>1;    int left=del(x),right=del(y);    //删除原来的节点     left=merge(left,x),right=merge(right,y);    left=merge(left,right);    //当把新节点插入左偏树时,可看做是两棵左偏树的合并     printf("%d\n",tree[left].strong);}int main(){    while(~scanf("%d",&n)){       for(register int i=1;i<=n;i++){          scanf("%d",&tree[i].strong);          tree[i].l=tree[i].r=tree[i].dis=0;          fa[i]=i;       }       scanf("%d",&m);       for(register int i=1;i<=m;i++){          int u,v;          scanf("%d%d",&u,&v);          int x=find(u),y=find(v);          if(x==y) printf("-1\n");          else solve(x,y);       }    }    return 0;}
原创粉丝点击