原題鏈接點擊打開鏈接
LGOJ1456點擊打開鏈接
題目大意呢就是
一開始有n只孤獨的猴子,然後他們要打m次架,每次打架都會把自己和朋友中最強的拉出來跟別人打,打完之後兩人戰鬥力就會減半。每次打完架就會成爲朋友(正所謂不打不相識....)。問每次打完架之後那倆猴子最強的朋友戰鬥力還有多少。朋友不打架,若朋友打架就輸出-1.
注意 有多組輸入數據
分析
1.每次要找自己和朋友中最強的,比較快的方法就是堆...
2.打完架之後就成了朋友,意味着需要經常把兩個堆進行合併
3.綜上兩點,需要用到一個很有意思的數據結構 左偏樹
(我也不知道哪個左撇子發明的這個東西....幹嘛不弄成右偏樹...)
對這就是一道非常基礎的左偏樹練習題
那麼關於左偏樹 稍作介紹
左偏樹是一棵二叉樹,且滿足堆的所有性質
左偏樹區別於普通堆的一個重要性質:
對於每一個節點,其左兒子的距離大於等於右兒子的距離
這裏的距離定義爲:該節點到其子樹的葉子節點最少進過的邊數。其中葉子節點(也稱外節點)的距離爲0,空節點爲-1。
利用上面的性質,每次插入都歸到右邊,可以在LogN的時間裏實現堆的合併
具體學習可以參考2005年國家集訓隊論文 黃源河:《左偏樹的性質及其應用》
到這裏MonkeyKing的問題就可以完美的解決了....
去找至尊寶玩了....再見........
var i,j,k,n,m,x,y,s,newroot,xx,yy,sx,sy,l,r,tt:longint;
v,left,right,fa,dis:array[-10..300007]of longint;
function max(a,b:longint):longint;
begin
if a>b then exit(a); exit(b);
end;
function getfa(x:longint):longint;
begin
while fa[x]<>0 do x:=fa[x];
exit(x);
end;
procedure swap(var x,y:longint);
var t:longint;
begin
t:=x; x:=y; y:=t;
end;
function Merge(r1,r2:longint):longint;
begin
if r1=0 then exit(r2);
if r2=0 then exit(r1);
if v[r1]<v[r2] then swap(r1,r2);
right[r1]:=Merge(right[r1],r2);
fa[right[r1]]:=r1;
if dis[left[r1]]<dis[right[r1]] then swap(left[r1],right[r1]);
dis[r1]:=dis[right[r1]]+1;
exit(r1);
end;
function delete(root:longint):longint;
var l,r:longint;
begin
fa[left[root]]:=0;
fa[right[root]]:=0;
dis[root]:=0;
l:=left[root]; left[root]:=0;
r:=right[root]; right[root]:=0;
exit(Merge(l,r));
end;
begin
while not eof do
begin
readln(n);
for i:=1 to n do
begin readln(v[i]);
fa[i]:=0;
left[i]:=0; right[i]:=0;
dis[i]:=0; end;
dis[0]:=-1;
readln(m);
for i:=1 to m do
begin
readln(x,y);
xx:=getfa(x); yy:=getfa(y);
if xx=yy then begin writeln(-1); continue; end;
v[xx]:=trunc(v[xx]/2);
v[yy]:=trunc(v[yy]/2);
l:=delete(xx); r:=delete(yy);
l:=Merge(l,xx); r:=Merge(r,yy);
l:=Merge(l,r);
writeln(v[l]);
end;
end;
end.