分析
考试的时候sb了......我们发现可以按照先序遍历将一棵树变成一个序列,而不需要删的数的数量便是最长上升子序列的长度,但是还有一个问题就是如果在5和7之间有3个空的位置就无法填入合法的数,但是按照此方法会将5和7划归为合法的。所以我们考虑将第i个数的权值变为原来的权值减去i,然后求一个最长不降子序列就行了。但是由于求解是n^2的,所以我们考虑用线段树来进行优化。我们建立一棵权值线段树,按顺序将其权值的位置变为其dp值,然后对于每次更新即为目前线段树中的权值不大于i的点的dp值的最大值。最终答案即为n减去这个最长不下降子序列的长度。
代码
#include<bits/stdc++.h>
using namespace std;
int n,a[],b[],son[][],d[],cnt,dp[],sum;
map<int,int>id;
inline void dfs(int x){
if(son[x][])dfs(son[x][]);cnt++;a[cnt]=d[x]-cnt;
if(son[x][])dfs(son[x][]);return;
}
inline void build(int le,int ri,int wh,int pl,int sum){
d[wh]=max(d[wh],sum);if(le==ri)return;int mid=(le+ri)>>;
if(mid>=pl)build(le,mid,wh<<,pl,sum);
else build(mid+,ri,wh<<|,pl,sum);
}
inline int q(int le,int ri,int x,int y,int wh){
if(le>=x&&ri<=y)return d[wh];int mid=(le+ri)>>,ans=;
if(mid>=x)ans=max(ans,q(le,mid,x,y,wh<<));
if(mid<y)ans=max(ans,q(mid+,ri,x,y,wh<<|));
return ans;
}
int main(){
int i,x,y;scanf("%d",&n);for(i=;i<=n;i++)scanf("%d",&d[i]);
for(i=;i<=n;i++)scanf("%d%d",&x,&y),son[x][y]=i;
dfs();memset(d,,sizeof(d));for(i=;i<=cnt;i++)b[i]=a[i];
sort(b+,b+cnt+);for(i=;i<=n;i++)if(!id[b[i]])id[b[i]]=++sum;
build(,sum,,id[a[]],);for(i=;i<=cnt;i++)
x=q(,sum,,id[a[i]],),build(,sum,,id[a[i]],x+);
printf("%d\n",n-q(,sum,,sum,));
return ;
}