C. 洗
小S有一个(n)个节点的二叉树。每个节点上有一个权值。节点从(1)开始编号。
现在,小S打算把这个二叉树改造成一个二叉排序树。二叉排序树的定义是,对于每个节点,它的权值比它左儿子的权值要大,但是比右儿子要小。
现在,他想要修改某些节点的权值,使得它成为一个二叉排序树。请问最小的修改次数是多少。
输入格式
第一行一个整数(n)。
接下来一行(n)个数,第(i)个表示(i)号节点的权值。
接下来 (n - 1)行,每行两个非负整数f, x,第(i)行描述结点(i + 1)的父亲编号(f),以及父子关系(x),((x = 0) 表示(i + 1)为左儿子,(x = 1)表示(i + 1)为右儿子)。
保证一号点一定是全树的根。
输出格式
一行一个整数,表示最小修改次数。
样例
样例一
input
3
2 2 2
1 0
1 1
output
2
约定与限制
对于(20\%) 的数据,满足 $ n le 10,a_ile 100$。
对于(40\%) 的数据,满足 (n le 100,a_ile 200)。
对于(60\%) 的数据,满足 (nle 2000)。
对于(100\%) 的数据 ,满足 (1 le n le 10^5,a_i< 2^{31}) 。
时间限制:1s
空间限制:512MB
解题报告
(100pts)思路
这道题目,题目给我们一个关键信息,就是要把二叉树变成合法的二叉排序树。
二叉排序树,具有的性质,就是中序遍历后的序列,具有严格单调递增的性质。
既然如此,我们为什么不把这题的结构,通过DFS,进行中序遍历,从树结构变成线性结构。
此时,我们的题面,改成了,在一个长度为(n)的序列,修改尽量少的点的值,使得整个序列严格单调递增。
那么此时,我们来观察一组数据.
1 4 2 5 7
我们发现,在这里.
1,5,7
并不需要动,而
4 2
应该改成
2 3
使得最终序列为
1 2 3 5 7
那么根据上面的例子,我们发现了什么。
我们在这个序列里面,需要修改的数总数,就是序列长度减去不满足不下降子序列的数。
而这里,我们的不下降子序列的定义,却有所不同。
原序列有两个点,分别为(i,j),且此时满足(i < j)
那么如果说,这两个点要在不下降子序列中,必须满足
否则这两个数,中间无法放置其他数。(若(i,j)紧靠,满足上面性质)
无法构造成为合法序列。
然后我们把不等式可以转换为:
因此所谓的不下降子序列,其实就是指的是,不用修改值的数列,因为他们已经满足了单调性质,改了没有任何用处。
而其他不满足的,都可以通过自身修改,使得整个序列最终成为严格单调递增的序列。
代码解析
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+20;
int n,a[N],f[N],cnt,ans,s[N],t[N][2];
inline void dfs(int x)//中序遍历
{
if (t[x][0])//先遍历左儿子
dfs(t[x][0]);
s[++cnt]=a[x];//中序遍历,计算DFS序
if (t[x][1])//最后遍历右儿子
dfs(t[x][1]);
}
inline void init()
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
for(int i=2; i<=n; i++)
{
int f,x;
scanf("%d%d",&f,&x);
if (x==1)//右儿子最后遍历
t[f][1]=i;
else//左儿子最先遍历
t[f][0]=i;
}
}
inline void work()
{
for(int i=1; i<=n; i++)//不等式变形
s[i]-=i;
f[ans=1]=s[1];
for(int i=2; i<=n; i++)//求解不下降子序列
if (s[i]>=f[ans])
f[++ans]=s[i];
else
f[upper_bound(f+1,f+1+ans,s[i])-f]=s[i];
printf("%d
",n-ans);
}
signed main()
{
init();
dfs(1);
work();
return 0;
}