长乐一中 Day2 T1 改造二叉树(luogu 3365 20分)

1.改造二叉树
【题目描述】
小Y在学树论时看到了有关二叉树的介绍:在计算机科学中,二叉树是每个结点最多有两个子结点的有序树。通常子结点被称作“左孩子”和“右孩子”。二叉树被用作二叉搜索树和二叉堆。随后他又和他人讨论起了二叉搜索树。
什么是二叉搜索树呢?二叉搜索树首先是一棵二叉树。设key[p]表示结点p上的数值。对于其中的每个结点p,若其存在左孩子lch,则key[p]>key[lch];若其存在右孩子rch,则key[p]<key[rch];注意,本题中的二叉搜索树应满足对于所有结点,其左子树中的key小于当前结点的key,其右子树中的key大于当前结点的key。
小Y与他人讨论的内容则是,现在给定一棵二叉树,可以任意修改结点的数值。修改一个结点的数值算作一次修改,且这个结点不能再被修改。若要将其变成一棵二叉搜索树,且任意时刻结点的数值必须是整数(可以是负整数或0),所要的最少修改次数。
相信这一定难不倒你!请帮助小Y解决这个问题吧。

【输入格式】
第一行一个正整数n表示二叉树结点数。结点从1~n进行编号。
第二行n个正整数用空格分隔开,第i个数ai表示结点i的原始数值。
此后n - 1行每行两个非负整数fa, ch,第i + 2行描述结点i + 1的父亲编号fa,以及父子关系ch,(ch = 0 表示i + 1为左儿子,ch = 1表示i + 1为右儿子)。
结点1一定是二叉树的根。

【输出格式】
仅一行包含一个整数,表示最少的修改次数。

【样例输入】
3
2 2 2
1 0
1 1

【样例输出】
2

【数据范围】
20 % :n <= 10 , ai <= 100.
40 % :n <= 100 , ai <= 200
60 % :n <= 2000 .
100 % :n <= 10 ^ 5 , ai < 2 ^ 31.

这题……又是谁出的模拟题

考虑二叉搜索树的性质,它的中序遍历一定是严格单调递增的。所以理论方法应该是求出这棵树的中序遍历,然后求出它的最长上升子序列,然后用长度n减去就行了。但这样是错的,因为有整数限制,就是权值必须是整数,所以遇到像1 2 0 3 4这样的数据,我们不能把0改成2.5,然后就挂了。那怎么办呢,题解给出了一种映射的方法:一个常见的将严格递增整数序列映射成非严格递增整 数序列的技巧就是将如下序列:
a1, a2, a3, a4 … an 映射成:
a1 - 1, a2 - 2, a3 - 3, a4 - 4 … an - n.
证明就是
在这里插入图片描述
来自洛谷asuldb。
然后求序列我是用线段树去优化dp,因为ai很大,所以我离散化了它。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#define int long long
using namespace std;
struct node
{
	int l,r,sum;
}s[100004];
struct node1
{
	int l,r,sum;
}t[100004*4];
int n,fa,ch,a[100004],xu[100004],num,len,b[100004],f[100004],ans;
int mmax(int a,int b)
{
	return a>b?a:b;
}
void dfs(int x)//求中序遍历
{
	if(s[x].l)
	{
		dfs(s[x].l);
	}
	xu[++num]=s[x].sum-num;//映射
	if(s[x].r)
	{
		dfs(s[x].r);
	}
}
void build(int rt,int l,int r)
{
	t[rt].l=l;
	t[rt].r=r;
	if(l==r)
	{
		t[rt].sum=0;
		return;
	}
	int mid=(l+r)>>1;
	build(rt*2,l,mid);
	build(rt*2+1,mid+1,r);
	t[rt].sum=mmax(t[rt*2].sum,t[rt*2+1].sum);
}
void upd(int rt,int x,int zhi)
{
	if(t[rt].l==x&&t[rt].r==x)
	{
		t[rt].sum=zhi;
		return;
	}
	int mid=(t[rt].l+t[rt].r)>>1;
	if(x<=mid)
	{
		upd(rt*2,x,zhi);
	}
	else
	{
		upd(rt*2+1,x,zhi);
	}
	t[rt].sum=mmax(t[rt*2].sum,t[rt*2+1].sum);
}
int ask(int rt,int l,int r)
{
	if(r<l)return 0;
	if(l<=t[rt].l&&r>=t[rt].r)
	{
		return t[rt].sum;
	}
	int mid=(t[rt].l+t[rt].r)>>1;
	if(r<=mid)
	{
		return ask(rt*2,l,r);
	}
	else if(l>mid)
	{
		return ask(rt*2+1,l,r);
	}
	else return mmax(ask(rt*2,l,r),ask(rt*2+1,l,r));
}
signed main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		s[i].sum=a[i];
	}
	for(int i=2;i<=n;i++)
	{
		scanf("%lld %lld",&fa,&ch);
		if(ch==0)
		{
			s[fa].l=i;
		}
		else
		{
			s[fa].r=i;
		}
	}
	dfs(1);
	for(int i=1;i<=n;i++)
	{
		a[i]=xu[i];
	}
	sort(a+1,a+n+1);//离散化
	len=unique(a+1,a+n+1)-(a+1);
	for(int i=1;i<=n;i++)
	{
		b[i]=lower_bound(a+1,a+len+1,xu[i])-a;
	}
	build(1,1,n);
	for(int i=1;i<=n;i++)
	{
		int tem=ask(1,1,b[i])+1;//求子序列
		upd(1,b[i],tem);
		ans=max(ans,tem);
	}
	cout<<n-ans;
	//cin>>a[i];
}

猜你喜欢

转载自blog.csdn.net/qq_37073764/article/details/83451273
今日推荐