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
6
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 为根节点, y 为深度。
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.