1. 程式人生 > >長樂一中 Day2 T1 改造二叉樹(luogu 3365 20分)

長樂一中 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];
}