NOIp模拟赛-改造二叉树

题意:

给定一个二叉树和节点值,修改一个节点的费用为1,求最小修改成二叉搜索树的费用。

思路:

  本题应该考虑二叉搜索树的性质,一棵二叉搜索树的中序遍历是一个严格上升的子序列,那么这道题就转换成求一个求这个树中序遍历后的最长上升子序列了,但是出于一些数据,这道题这样求只能拿部分分,而正解是先中序遍历,然后每个节点在线性数组中就有了一个位置,那么这个节点的值还要减去在自己在数组中的位置,那么这是为什么。设原数为a[i],中序遍历位置为i,那么就是a[i]-i=t,那么这个t表示的就是这个值离自己中序遍历的位置的距离.
      如果t=0,那么a[i]=i,说明这个数就在原位置上.
      如果t>0,说明a[i]的位置在原位置的右侧.
      如果t<0,说明a[i]的位置在原位置的左侧.
      那么问题首先是变成了求t的最长不降,为什么是最长不降,假如一个t序列,0 1 0 ,那么说明1和3数在自己的位置,而可以任意改变他们两个中间的数字,使成为上升,如果t序列是0 -3 1,如果1和3号位置是上升那么更可以改变他们中间的数字使a[i]成为上升(当然两边的t不一定是0,只要符合不降都可以改变中间的数),所以原问题就变成了求二叉树的中序遍历的最长不降序列.
求LIS时要用 nlog(n) 的算法
代码:
program pro;
type
  tre=record
    lc,rc:longint;
  end;

var
  f,g:array[0..201000] of longint;
  a,b:array[0..201000] of longint;
  tot,n:longint;
  tree:array[0..201000] of tre;

procedure openf;
begin
        assign(input,'bst.in'); reset(input);
        assign(output,'bst.out'); rewrite(output);
end;

procedure closef;
begin
        close(input);
        close(output);
end;

procedure dfs(t:longint);
begin
        if t=0 then exit;
        dfs(tree[t].lc);
        inc(b[0]);
        b[b[0]]:=a[t];
        dfs(tree[t].rc);
end;

procedure init;
var
        i,fa,t:longint;
begin
        readln(n);
        for i:=1 to n do read(a[i]);
        for i:=2 to n do
        begin
                readln(fa,t);
                if t=0 then tree[fa].lc:=i;
                if t=1 then tree[fa].rc:=i;
        end;
        dfs(1);
end;

procedure main;
var
        i,l,r,mid:longint;
begin
        for i:=1 to n do b[i]:=b[i]-i;

        tot:=0;
        g[0]:=-maxlongint;
        for i:=1 to n do
        begin
                if b[i]>=g[tot] then
                begin
                        inc(tot);
                        g[tot]:=b[i];
                        continue;
                end;

                l:=1; r:=tot;
                while l<r do
                begin
                        mid:=(l+r) shr 1;
                        if b[i]>=g[mid] then l:=mid+1
                        else r:=mid;
                end;
                g[l]:=b[i];
        end;
        writeln(n-tot);
end;

begin
        openf;
        init;
        main;
        closef;
end.
转自: Eme-sina博客

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值