[HDU1512]Monkey King

原题链接点击打开链接

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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值