[Day2] C. 洗

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)

那么如果说,这两个点要在不下降子序列中,必须满足

[s[j]-s[i] ge j-i ]

否则这两个数,中间无法放置其他数。(若(i,j)紧靠,满足上面性质)

无法构造成为合法序列。

然后我们把不等式可以转换为:

[s[j]-j ge s[i]-i ]

因此所谓的不下降子序列,其实就是指的是,不用修改值的数列,因为他们已经满足了单调性质,改了没有任何用处。

而其他不满足的,都可以通过自身修改,使得整个序列最终成为严格单调递增的序列。


代码解析

#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;
}
原文地址:https://www.cnblogs.com/gzh-red/p/13769628.html