改造二叉树——题解

题目大意

给出一颗二叉树及其 N 个节点的权值,每个节点权值最多修改一次,且只能修改成整数。求最小的修改次数,使这棵二叉树成为排序二叉树。
N <= 100000

首先,排序二叉树是一种左儿子权值严格小于父亲,右儿子权值严格大于父亲的二叉树( B S T )。所谓“排序”,就是先序遍历排序二叉树,得到的是一个严格递增的序列。

那么我们显然要先先序遍历一趟,构造出 A 数组,那么题目就变成求使一个序列变为严格递增所需的最少修改次数
所以答案就为 N L I S ( )

但是,

题目要求每个节点的权值只能改为整数!只要有“卡死”的重复元素,就完蛋了
如这个样例就崩溃了:
N = 4        A : 1     2     2     3

L I S = 3       W r o n g   A n s w e r
A n s w e r = 2

我们再思考一下:
修改完后,显然
A 1 < A 2 < A 3 < A i < A i + 1 < A N

A 1 + 1 <= A 2   , A 2 + 1 <= A 3 ,   A i + 1 <= A i + 1   A N 1 + 1 <= A N
所以显然对于 i , j ϵ [ 1 , n ] ( i <= j ) ,有
A j A i >= j i ,即 A j j >= A i i
那如果将每个Ai抠掉i,则
A 1 1 <= A 2 2 <= A 3 3 <= A i i <= A i + 1 ( i + 1 ) <= A N N
此时,就没有重复的元素影响了
只要 O ( N l o g ) 求一趟最长不降子序列就OK了

#include<cstdio>
#define LL long long
using namespace std;
const int maxn=(1e5)+5;
int n,tal;LL que[maxn],a[maxn];
struct ff{
    int L,R,x;
}c[maxn];
char gt(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
int read(){
    int ret=0;bool f=0;char ch=gt();
    while(ch<'0'||ch>'9') f|=(ch=='-'),ch=gt();
    while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=gt();
    return f?-ret:ret;
}
void DFS(int x){
    if(c[x].L) DFS(c[x].L);
    a[++a[0]]=c[x].x;
    if(c[x].R) DFS(c[x].R);
}
void find(LL x){
    if(x>=que[tal]){que[++tal]=x;return;}
    int L=1,R=tal,mid;
    while(L<=R){
        mid=L+R>>1;
        if(que[mid-1]<=x&&x<que[mid]){que[mid]=x;return;}
        if(x<que[mid-1]) R=mid-1;else L=mid+1; 
    }
}
int main(){
    n=read();
    for(int i=1;i<=n;i++) c[i].x=read();
    for(int i=2;i<=n;i++){
        int fa=read(),flg=read();
        if(flg) c[fa].R=i;else c[fa].L=i;
    }
    DFS(1);
    que[0]=-((LL)1<<60);
    for(int i=1;i<=n;i++) find(a[i]-i);
    printf("%d\n",n-tal);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42403731/article/details/81059503