• [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;
    }
    
  • 相关阅读:
    提升request模块的效率--线程池
    selenium的用法
    SVN 常用命令
    SVN 常见问题及解决方法
    Makefile 详解
    开心一刻(一)
    Vim配置及其他注意事项
    彩虹表
    C++学习之STL(二)String
    C++学习之STL(一)vector
  • 原文地址:https://www.cnblogs.com/gzh-red/p/13769628.html
Copyright © 2020-2023  润新知