P1864 [NOI2009] 二叉查找树 题解
二叉查找树
首先该树的中序遍历是唯一可以确定的(直接按照数据值排序即可)。
然后,因为权值可以被修改成一切实数,故我们完全可以把权值离散化掉。
于是我们现在可以设置一个 DP 状态 \(f[l,r,lim]\) 表示:
区间 \([l,r]\) 中的所有东西构成了一棵子树,且树中最小权值不小于 \(lim\) 的最优方案。
然后就枚举根转移即可。转移的时候就可以看作是子树内所有东西被整体提高了一层,所以直接增加 \(sum[l,r]\)(意为区间 \([l,r]\) 中的所有数据值之和)即可。同时,如果有当前枚举的根的权值不小于 \(lim\),显然就可以不修改,但是两边儿子的权值就必须比它大;否则则必须修改,两边儿子的权值下限还是 \(lim\)(因为根的权值可以被修改成一个略大于 \(lim\) 的实数)。
则时间复杂度 \(O(n^4)\)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int INF=0x3f3f3f3f;
const int N=110;
struct node{
int v,w,c;
}a[N];
int n,m;
int sum[N],f[N][N][N];
int dfs(int l,int r,int lim){
if(l>r) return 0;
if(f[l][r][lim]!=-1) return f[l][r][lim];
int &now=f[l][r][lim];
now=INF;
for(int i=l;i<=r;i++){
if(a[i].w>=lim) now=min(now,dfs(l,i-1,a[i].w)+dfs(i+1,r,a[i].w)+sum[r]-sum[l-1]);
now=min(now,dfs(l,i-1,lim)+dfs(i+1,r,lim)+m+sum[r]-sum[l-1]);
}
return now;
}
bool cmp1(node x,node y){return x.w<y.w;}
bool cmp2(node x,node y){return x.v<y.v;}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m;
memset(f,-1,sizeof(f));
for(int i=1;i<=n;i++) cin>>a[i].v;
for(int i=1;i<=n;i++) cin>>a[i].w;
for(int i=1;i<=n;i++) cin>>a[i].c;
sort(a+1,a+n+1,cmp1);
for(int i=1;i<=n;i++) a[i].w=i;
sort(a+1,a+n+1,cmp2);
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i].c;
cout<<dfs(1,n,1);
return 0;
}