P1864 [NOI2009] 二叉查找树

一 . 分析

数据值不变 \(\Rightarrow\) 二叉树的中序遍历不变

修改权值,每次只修改树的形态,相当于左旋,右旋,会使节点的深度改变

权值可以修改为任意实数,所以权值之间可以无限接近,看作相同的。因此,权值对题目答案没有影响,可以将权值离散化到 \([1,n]\)

二、DP

\(f[i][j][k]\) 表示以中序遍历区间 \([i,j]\) 内的节点建成的子树中,节点权值 \(\geq k\) 的最小代价

转移时枚举一个点 \(k\) 作为根节点,两边的区间作为左、右子树,整棵树下挪一个深度,代价增加量为区间内的访问频度之和,用 sum 数组预处理

当 k 的权值 \(\geq w\)

\[f[i][j][w]=min(f[i][j][w],f[i][k-1][w_k]+f[k+1][j][w_k]+sum[j]-sum[i-1]) \]

当 k 的权值 \(< w\)

\[f[i][j][w]=min(f[i][j][w],f[i][k-1][w]+f[k+1][j][w]+sum[j]-sum[i-1]+k) \]

#include<bits/stdc++.h>
using namespace std;
int n,K;
long long f[75][75][75],sum[75];
struct node{
	int s,q,p;
}a[75];
bool cmp1(node x,node y){
	return x.q<y.q;
}
bool cmp2(node x,node y){
	return x.s<y.s;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>K;
	for(int i=1;i<=n;i++)
		cin>>a[i].s;
	for(int i=1;i<=n;i++)
		cin>>a[i].q;
	for(int i=1;i<=n;i++)
		cin>>a[i].p;
	sort(a+1,a+n+1,cmp1);
	for(int i=1;i<=n;i++)
		a[i].q=i;
	sort(a+1,a+n+1,cmp2);
	for(int i=1;i<=n;i++) 
		sum[i]=sum[i-1]+a[i].p;	
	for(int l=1;l<=n;l++){
		for(int i=1;i+l-1<=n;i++){
			int j=i+l-1;
			for(int w=1;w<=n;w++){
				f[i][j][w]=1e18;
				for(int k=i;k<=j;k++){
					f[i][j][w]=min(f[i][j][w],f[i][k-1][w]+f[k+1][j][w]+sum[j]-sum[i-1]+K);
					if(a[k].q>=w) f[i][j][w]=min(f[i][j][w],f[i][k-1][a[k].q]+f[k+1][j][a[k].q]+sum[j]-sum[i-1]);
				}
			}
		}
	}
	cout<<f[1][n][1]<<'\n';
	return 0;
}
posted @ 2026-06-13 09:45  Aguanenti  阅读(1)  评论(0)    收藏  举报