ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[NOI2009] 二叉查找树 题解

2021-08-02 11:03:54  阅读:158  来源: 互联网

标签:结点 int 题解 sum 二叉 访问 maxn 权值 NOI2009


[NOI2009] 二叉查找树 题解

题解:

通过题目中,对树的描述:按照权值从小到大顺序插入结点所得到的按照数据值排序的二叉查找树,可以想到这是一棵 treap。改变权值相当于 treap 的左旋右旋,通过旋转会使结点的深度发生改变,从而影响最终的答案。

根据 treap 的性质,因为数据值不变,那么树的中序遍历也不会变, 而二叉搜索树的的中序遍历,就是数据值从小到大排序的结果。

接下来就可以 dp 了。

因为权值可以取任意的实数,而且最后的答案与权值无关,方便之后的处理可以将权值离散化。

状态:设 \(f[i][j][k]\)​​​​​ 表示 \(i\)​​​​ 到 \(j\)​​​ ​​​节点组成了一棵树,其中权值都 \(>= k\)​​​, 所需要的代价为多少。

(第三维的权值是有离散化的,看代码),答案就是 \(f[1][n][1]\)​。

转移:从 \(i\) 到 \(j\) 枚举一个 \(t\),假设让 \(t\) 来做根节点, \(t\) 结点的权值都比它的儿子结点的权值要小 (题目性质)。

接下来考虑 \(t\) 这个节点的权值是否需要改变。

如果 \(t\)​ 结点已经 $ >= k$​ 了,不需要修改:

\(f[i][j][k] = min(f[i][j][k], f[i][t - 1][t的权值] + f[t + 1][j][t的权值] + i 到 j的访问频率之和)\)

关于这个转移方程:

  1. 虽然题目中说,“所有结点的权值必须仍保持互不相同”,但权值为实数类型,两个权值可以无限接近而不相同,所以这个条件并无约束作用,转移方程中写的是“t的权值”。

  2. 转移方程之中“i 到 j的访问频率之和”,计算的是访问代价。

    访问代价 = 深度 $\times $​​ 访问频率。当我们一层层计算时,每次都会将当前子树内的访问频率加一遍,累积起来就是访问代价。

如果要修改:

\(f[i][j][k] = min(f[i][j][k], f[i][t - 1][k] + f[t + 1][j][k] + i 到 j的访问频率之和)\)

/*
Date:2021.8.2
Source:luogu 1864
konwledge:对treap的理解 + dp 
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define orz cout << "AK IOI"

using namespace std;
const int maxn = 80;
const int maxm = 4e5 + 10;

inline int read()
{
	int f = 0, x = 0; char ch = getchar();
	while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) x = x * 10 + (ch ^ 48), ch = getchar();
	return f ? -x : x;
}
inline void print(int X)
{
	if(X < 0) {X = ~(X - 1); putchar('-');}
	if(X > 9) print(X / 10);
	putchar(X % 10 + '0');
}
int n, K, val[maxn], sum[maxn], f[maxn][maxn][maxn]; 
struct node{
	int num, val, sum;
}a[maxn];
bool cmp(node a, node b)
{
	return a.num < b.num;
}
int main()
{
	n = read(), K = read();
	for(int i = 1; i <= n; i++) a[i].num = read();
	for(int i = 1; i <= n; i++) val[i] = a[i].val = read();
	for(int i = 1; i <= n; i++) a[i].sum = read();
	sort(a + 1, a + n + 1, cmp);
	sort(val + 1, val + n + 1);
	for(int i = 1; i <= n; i++)
	{
		a[i].val = lower_bound(val + 1, val + n + 1, a[i].val) - val;//离散化
		sum[i] = sum[i - 1] + a[i].sum; 
	}
	memset(f, 63, sizeof f);
	for(int i = 1; i <= n + 1; i++)
		for(int j = 1; j <= n; j++) f[i][i - 1][j] = 0;
	for(int i = n; i >= 1; i--)
    	for(int j = i; j <= n; j++)
        	for(int k = 1; k <= n; k++)
          		for(int t = i; t <= j; t++)
				{
              		if(a[t].val >= k) 
                	f[i][j][k] = min(f[i][j][k], f[i][t - 1][a[t].val] + f[t + 1][j][a[t].val] + sum[j] - sum[i - 1]);
              		f[i][j][k] = min(f[i][j][k], f[i][t - 1][k] + f[t + 1][j][k] + K + sum[j] - sum[i - 1]);
          		}
	printf("%d", f[1][n][1]);
	return 0;
}

感谢 https://www.luogu.com.cn/blog/wangwangwangwangwang/solution-p1864

标签:结点,int,题解,sum,二叉,访问,maxn,权值,NOI2009
来源: https://www.cnblogs.com/yangchengcheng/p/15089004.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有