1. 程式人生 > >HDU 1512 Monkey King(左偏樹)

HDU 1512 Monkey King(左偏樹)

題意:
n 只猴子,每隻猴子都有一個力量,開始時互相都不認識,它們之間發生 m 次爭鬥,每次發生a,b發生爭鬥時,a,b會從它們認識的猴子中選出一個最強的,並變為這兩隻猴子發生爭鬥,打完之後這兩個猴子就互相認識,並且力量減半,如果a,b互相認識就輸出1,否則輸出認識的猴子中最大的力量值。

題解:
左偏樹。

AC程式碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
struct node
{
    int l,r;
    int
dis; int val; }ltree[100005]; int par[100005]; int getfather(int x) { return par[x] == x?x:par[x]=getfather(par[x]); } int merge(int x,int y)//返回合併後的根 { int l,r; //插入的樹為空時直接返回x (y),也就是合併完的樹 if(x==0)return y; if(y==0)return x; if(ltree[x].val<ltree[y].val) //大頂堆 { swap(x,y); } ltree[x].r = merge(ltree[x].r,y);//遞迴合併右子樹 和 y
l = ltree[x].l; r = ltree[x].r; par[r] = x; //並查集合並,更新右子樹的根 if(ltree[l].dis<ltree[r].dis) { //必須遵守左偏樹的性質,左節點的距離不小於右節點的距離 swap(ltree[x].l,ltree[x].r); } if(ltree[x].r==0)//如果沒有右子樹 則距離為0 { ltree[x].dis = 0; } else ltree[x].dis = ltree[ltree[x].r].dis+1
; return x; } int pop(int x)//返回刪除根以後左右子樹合併的根 { int l,r; l = ltree[x].l; r = ltree[x].r; //因為要暫時刪掉根,所以左右子樹先作為根 par[l] = l; par[r] = r; ltree[x].l = 0; ltree[x].r = 0; ltree[x].dis = 0; return merge(l,r); } int main() { int n,m; while(cin>>n) { for(int i=1;i<=n;i++) { scanf("%d",&ltree[i].val); ltree[i].l = 0; ltree[i].r = 0; ltree[i].dis = 0; } for(int i=1;i<=n;i++)par[i] = i; scanf("%d",&m); int a,b; int fa,fb; int l,r; while(m--) { scanf("%d%d",&a,&b); fa = getfather(a); fb = getfather(b); if(fa==fb)puts("-1"); else { //單挑後減半 ltree[fa].val /= 2; ltree[fb].val /= 2; //刪除最大的兩個值,再與新的值合併 l = pop(fa),r=pop(fb); l = merge(l,fa); r = merge(r,fb); l = merge(l,r); printf("%d\n",ltree[l].val); } } } return 0; }