@AkamemakA
2022-07-22T12:44:32.000000Z
字数 2700
阅读 120
题解
杂题
数据结构
一棵二叉树为一颗二叉排序树,当且仅当所有的节点(叶子结点除外)的左子树上的所有点的值都小于此节点的值,且所有右节点的值都大于此节点的值。
有一棵二叉树,每个节点有一个权值。问最少要修改多少个点的权值(只能修改为整数),才能使这棵树变为一颗二叉排序树。输出最小修改次数。
第一行一个整数,第二行个整数表示个点的权值。接下来行输入两个数,第行第一个数表示节点的父亲为,第二个数表示左右关系。若,则为的左儿子。若 ,则为右儿子。
3
2 2 2
1 0
1 1
2
将节点的值修改为,将节点的值修改为。
对于二叉排序树的定义,我们不难想到如果对于一颗二叉排序树进行中序遍历,那么得到的序列一定是一个严格递增的序列。
因此,我们要做的就是对于输入的一棵树,将其进行中序遍历,再用最小的操作次数将其修改为严格递增的即可。
所以不难想到,只需要找到遍历出的数组的最长上升子序列的长度,再用总长减去即可。
但是这样做会有一个问题,例如遍历出来的权值为:,那么按上述做法,找到了最长上升子序列,即要修改的值。但题中有规定,权值只能修改为整数,因此只修改是无法达到要求的。
因此,我们来做进一步考虑。
不难发现,如果要将与之间的所有数改为递增,那么必须满足一个条件:
我们再将此式化简,得到:
我们定义,那么这个问题就很好办了:我们只需要求出数组,再找到的最长不下降子序列()的长度,再用总长减去即可。
#include<iostream>
using namespace std;
const int N=2e5+5;
int n;
int a[N],f[N],b[N],tot;
int tree[N][2];
inline void dfs(int u){
if(tree[u][0]) dfs(tree[u][0]);
b[++tot]=a[u]-tot; //同时求出b数组
if(tree[u][1]) dfs(tree[u][1]);
}
inline int lis(){
int res=0;
f[1]=1;
for(int i=2;i<=n;i++){
f[i]=1;
for(int j=1;j<i;j++)
if(b[i]>=b[j]) f[i]=max(f[i],f[j]+1);
res=max(res,f[i]);
}
return res; //LIS模板,没什么好说的
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=2;i<=n;i++){
int x,f;
cin>>x>>f;
tree[x][f]=i;
}
dfs(1); //中序遍历
cout<<n-lis();
}
不过普通的时间复杂度趋近于,但此题 ,因此,这份代码只能过%的数据。
接下来,我们考虑优化。
谈到优化,那就是令人脑袋疼的数据结构优化了。线段树、树状数组、都可以来解决此问题(玄学卡常就免了)。这里,我们来介绍树状数组的做法。
观察上述代码第行,我们发现,对于以结尾的是由之前的最大的转移而来。因此我们可以用树状数组维护最大值。具体理论可以参考其他大佬讲解。
对于每一个数,先查出小于这个数结尾的的的最大值,那么
然后再把以结尾的的最大值插入到树状数组中。利用树状数组维护这一过程。
由于非常大,普通的数组下标存不下。因此,要将离散化。
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
#define inv(x) (lower_bound(v.begin(),v.end(),x)-v.begin()+1) //求离散化后的映射值
const int N=1e5+5;
int n,a[N];
int tree[N][2];
int b[N],tot,c[N];
int f[N];
vector<int> v; //离散数组
inline int lowbit(int x){
return x& (-x);
}
inline void dfs(int u){
if(tree[u][0]) dfs(tree[u][0]);
b[++tot]=a[u]-tot;
if(tree[u][1]) dfs(tree[u][1]);
}
inline void lisan(){//离散化
for(int i=1;i<=n;i++)
v.push_back(b[i]);
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
}
inline int query(int x){ //找到小于b[x]结尾的LIS的最大值
int res=0;
for(int i=x;i;i-=lowbit(i))
res=max(res,c[i]);
return res;
}
inline void updata(int x,int y){//更新最大值
for(int i=x;i<=n;i+=lowbit(i))
c[i]=max(c[i],y);
}
inline int lis(){
int res=0;
for(int i=1;i<=n;i++){
b[i]=inv(b[i]);
f[i]=query(b[i])+1;
updata(b[i],f[i]);
res=max(res,f[i]);
}
return res;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=2;i<=n;i++){
int u,x;
cin>>u>>x;
tree[u][x]=i;
}
dfs(1);
lisan();
cout<<n-lis();
}
完结撒花~