解题报告 APIO 2007 风铃

No.1风铃  APIO 2007

     风铃是一种多层的装饰品,一般挂在天花板上。每个风铃都包含一些由竖直的线连起来的水平杆。每根杆的两端都有线连接,下面或者挂着另一根水平杆,或者挂着一个玩具。下面是一个风铃的例子:

        

     

但是maxingc是一个完美主义者(说到完美主义者,还真是恐怖啊,柯南剧场版的前两部凶手都是完美主义者啊),他对风铃的要求如下:

     (1) 所有的玩具都在同一层(也就是说,每个玩具到天花板之间的杆的个数是一样的)或至多相差一层。

    (2) 对于两个相差一层的玩具,左边的玩具比右边的玩具要更靠下一点。

 风铃可以按照下面的规则重新排列:任选一根杆,将杆两端的线“交换”。也就是解开一根杆左右两端的线,然后将它们分别绑到杆的另一端。注意这个操作不会改变下面的杆上线的排列顺序。

   Maxingc给窄森一个风铃,让他在1s中之内将它调整到所要求的样子,否则就不让他吃午饭,可怜的窄森怎么会在1s内完成,所以要求助于学oi的你。你只需在一秒钟内说出最少需要几次交换就行了。

   考虑上面的例子,上图中得风铃满足条件1,却不满足条件2。

   但是,我们可以通过下面的步骤把这个风铃变成一个符合要求的。

          

    

   

Input

  输入的第一行包含一个整数n (1 ≤ n ≤ 100 000),表示风铃中有多少根杆。接下来的n行描述杆的连接信息。这部分的第i行包含两个由空格分隔的整数li和ri,描述杆i的左右两端悬挂的东西。如果挂的是一个玩具,则对应的值为-1,否则为挂在下面的杆的编号。如果杆i下面挂有其它杆,则这些杆的编号将严格大于i。杆1位于风铃的顶部。

Output

  输出仅包含一个整数。表示最少需要多少次交换能使风铃满足Ike的条件。如果不可能满足,输出-1。

Sample Input

2 3

-1 4

5 6

-1 -1

-1 -1

-1 -1

Sample Output 

  2 

 

 

 

题目简述:

一根杆有两端,一端只能挂一个玩具或另一根杆,许多根杆和玩具组成一个风铃,你一次操作可以将一个杆的两端对调,并且不会影响下边的部分,求最少的操作次数,是风铃满足以下条件:(1)任意一对玩具到最顶上的杆的距离之差小于等于1;(2)任意一对玩具必满足距离大的在距离小的左边。如果不可能实现,输出-1

 

 

 

乍一看来,完全无从下手,只有先分析无解的情况。

首先,如果存在两个玩具深度之差大于1,由于交换操作不会更改玩具的深度,所以这样肯定是无解的。(话说我考试的时候,好像只判断出来了这一种)

然后,风铃是一个完全二叉树,因此N个节点的完全二叉树只可能有logN层,一旦递归深度超过了logN,那也无解。当然也可以理解为,如果风铃不是倒数第二层往上是满二叉树,那么一定不行。

然后,第三个无解的条件就是存在一个杆,杆的两侧都有深度差。

 

从这个例子,我们不难发现本题的玄机:

某个杆如果左边存在一个玩具,它的深度小于右边的某个玩具,那么这根杆的两端是必须要交换的。因为无论是该杆上边还是下边的操作都不会对它产生影响,而且它的上面是满二叉树,所以上边假设的情况只能通过交换这根杆来解决,所以这次交换是必须的,而且只需一次。

于是,我们的算法就呼之欲出了。

 

算法:

遍历这棵树,统计每根杆之下的最小深度和最大深度,如果出现无解的情况,就直接退出。如果出现要交换的情况,答案加1。遍历完毕,答案就出来了。

 

 

代码 Leve

type tree=record

  lc,rc,c,cnt:longint;

  d:boolean;

  end;

const maxn=100001;

var i,n,ans,x:longint;

    t:array[-1..maxn] of tree;

 

procedure swap(var a,b:longint);

var t:longint;

begin

  t:=a; a:=b; b:=t;

end;

 

procedure ty(k,y:longint);  // k 为根节点, 为深度。

var r,l:longint;

begin

  if k=-1 then exit;

  if ans=-1 then exit;

  if y>x then

    begin

      ans:=-1; exit;

    end;

 

  r:=t[k].rc; l:=t[k].lc;

  ty(l,y+1); ty(r,y+1);

  if ans=-1 then exit;

 

  if t[r].cnt>t[l].cnt then

    begin

      swap(t[k].rc,t[k].lc);

      swap(l,r);

      inc(ans);

    end;

 

  if t[l].c-t[r].c>1 then ans:=-1; //深度差 >1

  if (t[l].d=false) and (t[l].d=t[r].d) then ans:=-1;  //杆两侧都有深度差

  if ans=-1 then exit;

 

  t[k].cnt:=t[l].cnt+t[r].cnt;

  t[k].c:=t[l].c+1;

  if t[l].c<>t[r].c then t[k].d:=false else t[k].d:=true;

end;

begin

  assign(input,'Mobiles.in');

  assign(output,'Mobiles.out');

  reset(input);

  rewrite(output);

 

  readln(n);

  for i:=1 to n do 

    readln(t[i].lc,t[i].rc);

  t[-1].cnt:=1; 

  t[-1].c:=1; 

  t[-1].d:=true;

  x:=trunc(ln(n)/ln(2))+1;  //最大深度

  ty(1,1);

  writeln(ans);

close(input);

close(output);

end.

原文地址:https://www.cnblogs.com/SueMiller/p/2234687.html