【BZOJ1564】【NOI2009】二叉查找树(动态规划)
【BZOJ1564】【NOI2009】二叉查找树(动态规划)
题面
已知一棵特殊的二叉查找树。根据定义,该二叉查找树中每个结点的数据值都比它左儿子结点的数据值大,而比它右儿子结点的数据值小。
另一方面,这棵查找树中每个结点都有一个权值,每个结点的权值都比它的儿子结点的权值要小。
已知树中所有结点的数据值各不相同;所有结点的权值也各不相同。这时可得出这样一个有趣的结论:如果能够确定树中每个结点的数据值和权值,那么树的形态便可以唯一确定。因为这样的一棵树可以看成是按照权值从小到大顺序插入结点所得到的、按照数据值排序的二叉查找树。
一个结点在树中的深度定义为它到树根的距离加1。因此树的根结点的深度为1。
每个结点除了数据值和权值以外,还有一个访问频度。我们定义一个结点在树中的访问代价为它的访问频度乘以它在树中的深度。整棵树的访问代价定义为所有结点在树中的访问代价之和。
现在给定每个结点的数据值、权值和访问频度,你可以根据需要修改某些结点的权值,但每次修改你会付出K的额外修改代价。你可以把结点的权值改为任何实数,但是修改后所有结点的权值必须仍保持互不相同。现在你要解决的问题是,整棵树的访问代价与额外修改代价的和最小是多少?
输入输出格式
输入格式:
输入文件中的第一行为两个正整数N,K。其中:N表示结点的个数,K表示每次修改所需的额外修改代价。
接下来的一行为N个非负整数,表示每个结点的数据值。
再接下来的一行为N个非负整数,表示每个结点的权值。
再接下来的一行为N个非负整数,表示每个结点的访问频度。
其中:所有的数据值、权值、访问频度均不超过400000。每两个数之间都有一个空格分隔,且行尾没有空格。
输出格式:
输出文件中仅一行为一个数,即你所能得到的整棵树的访问代价与额外修改代价之和的最小值。
题解
我们知道,二叉查找树在拍平(也就是中序遍历)之后得到的序列是有序的
所以一般这类问题就可以考虑区间\(dp\)了。
因此,我们的第一想法设\(f[i][j]\)表示把\(i..j\)这一段构好树得到的最小代价。
首先我们不考虑权值的情况。
那么转移的时候,枚举一下树根,左右侧分别构树,作为当前根节点的儿子
同时所有点的深度都会加一,再额外计算一下这个值就可以了。
现在权值是可以修改的,如果自己想想,发现这棵二叉查找树是一个\(Treap\)
同时对于权值维护的是一个小根堆。另外,权值的大小没有意义,可以直接离散。
因此,在枚举根节点的时候,左右儿子对于当前根节点的影响一定是这段区间内权值的最小值所带来的影响。
所以,给状态加一维,设\(f[i][j][k]\)表示当前区间是\(i..j\),其中最小权值是\(k\)的二叉搜索树的最小代价。
考虑如何转移
对于区间,显然要先枚举当前的根节点,那么根节点一定就是最小权值,所以还枚举根节点的权值。
枚举左右子树的根节点的权值,这个权值一定要大于当前根节点的权值。
额外的费用只有是否修改当前根节点的权值。
所以,转移就是
\(f[i][j][k]=min(f[i][p-1][n]+f[p+1][j][m]+\sum_{l\in[i,j]}s[l]+[v[i]==k]·K)\)
这样复杂度是\(O(N^6)\)的(因为还要枚举左右子树的根节点的权值)
但是如果我们把状态的\(k\)改为所有值都不小于\(k\)的最小代价
这样就没有问题啦。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 75
inline int read()
{
RG int x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
struct Node{int v,key,s;}t[MAX];
bool operator<(Node a,Node b){return a.v<b.v;}
int S[MAX],n,K;
int f[MAX][MAX][MAX];
int main()
{
n=read();K=read();
for(int i=1;i<=n;++i)t[i].v=read();
for(int i=1;i<=n;++i)S[i]=t[i].key=read();
for(int i=1;i<=n;++i)t[i].s=read();
sort(&S[1],&S[n+1]);int len=unique(&S[1],&S[n+1])-S-1;
for(int i=1;i<=n;++i)t[i].key=lower_bound(&S[1],&S[len+1],t[i].key)-S;
sort(&t[1],&t[n+1]);
for(int i=1;i<=n;++i)S[i]=t[i].s+S[i-1];
memset(f,63,sizeof(f));
for(int i=1;i<=n+1;++i)
for(int j=0;j<=n;++j)
f[i][i-1][j]=0;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
f[i][i][j]=t[i].s+(t[i].key!=j)*K;
for(int k=n;k;--k)
for(int l=1;l<=n;++l)
for(int i=1,j=i+l-1;j<=n;++i,++j)
for(int p=i;p<=j;++p)
{
f[i][j][k]=min(f[i][j][k],f[i][p-1][k]+f[p+1][j][k]+S[j]-S[i-1]+K);
if(k<=t[p].key)f[i][j][k]=min(f[i][j][k],f[i][p-1][t[p].key]+f[p+1][j][t[p].key]+S[j]-S[i-1]);
}
printf("%d\n",f[1][n][1]);
return 0;
}
【BZOJ1564】【NOI2009】二叉查找树(动态规划)的更多相关文章
- [BZOJ1564][NOI2009]二叉查找树 树形dp 区间dp
1564: [NOI2009]二叉查找树 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 879 Solved: 612[Submit][Status] ...
- BZOJ1564 NOI2009二叉查找树(区间dp)
首先按数据值排序,那么连续一段区间的dfs序一定也是连续的. 将权值离散化,设f[i][j][k]为i到j区间内所有点的权值都>=k的最小代价,转移时枚举根考虑是否修改权值即可. #includ ...
- [BZOJ1564][NOI2009]二叉查找树(区间DP)
题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1564 分析: 首先因为每个点的数据值不变,所以无论树的形态如何变,树的中序遍历肯定不变 ...
- bzoj1564: [NOI2009]二叉查找树
dp. 首先这棵树是一个treap. 权值我们可以改成任意实数,所以权值只表示相互之间的大小关系,可以离散化. 树的中序遍历是肯定确定的. 用f[l][r][w]表示中序遍历为l到r,根的权值必须大于 ...
- BZOJ 1564: [NOI2009]二叉查找树( dp )
树的中序遍历是唯一的. 按照数据值处理出中序遍历后, dp(l, r, v)表示[l, r]组成的树, 树的所有节点的权值≥v的最小代价(离散化权值). 枚举m为根(p表示访问频率): 修改m的权值 ...
- bzoj 1564 [NOI2009]二叉查找树 区间DP
[NOI2009]二叉查找树 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 906 Solved: 630[Submit][Status][Discu ...
- P1864 [NOI2009]二叉查找树
链接P1864 [NOI2009]二叉查找树 这题还是蛮难的--是我菜. 题目描述中的一大堆其实就是在描述\(treap.\),考虑\(treap\)的一些性质: 首先不管怎么转,中序遍历是确定的,所 ...
- NOI2009 二叉查找树 【区间dp】
[NOI2009]二叉查找树 [问题描述] 已知一棵特殊的二叉查找树.根据定义,该二叉查找树中每个结点的数据值都比它左子树结点的数据值大,而比它右子树结点的数据值小.另一方面,这棵查找树中每个结点都有 ...
- BZOJ 1564 :[NOI2009]二叉查找树(树型DP)
二叉查找树 [题目描述] 已知一棵特殊的二叉查找树.根据定义,该二叉查找树中每个结点的数据值都比它左儿子结点的数据值大,而比它右儿子结点的数据值小. 另一方面,这棵查找树中每个结点都有一个权值,每个结 ...
- [洛谷P1864] NOI2009 二叉查找树
问题描述 已知一棵特殊的二叉查找树.根据定义,该二叉查找树中每个结点的数据值都比它左儿子结点的数据值大,而比它右儿子结点的数据值小. 另一方面,这棵查找树中每个结点都有一个权值,每个结点的权值都比它的 ...
随机推荐
- Jenkins传参修改jmeter的报告名称和详细报告地址
目前已经可以发送邮件了,我已经配置了Jenkins,但是有几个显示问题,待处理1.报告名称地址, 2详细报告地址没有做跳转 更改后为: 修改url 1.打开样式的jmeter-results-deta ...
- JAVA中commons-collections-3.2.1.jar包是干什么用的?
类似C++中的Boost库,对Java容器类型和算法的补充
- 【[AHOI2005]航线规划】
树剖维护边双 首先我们看到在整个过程中图是保证连通的,于是我们并不需要LCT来维护连通性 而这些询问询问的是两个点之间关键路径的数量,也就是无论怎么走都必须走的数量,显然这就是两点之间的割边的数量 由 ...
- 【题解】洛谷P4281 [AHOI2008] 紧急集合(求三个点LCA)
洛谷P4281:https://www.luogu.org/problemnew/show/P4281 思路 答案所在的点必定是三个人所在点之间路径上的一点 本蒟蒻一开始的想法是:先求出2个点之间的L ...
- UITableView控件Protocell的Identifier设置 注意事项
1. 注意:如果想使用Subtitle类型的单元格,需在Storyboard中将Protocell设置为subtitle类型,且Protocell的identifier必须与ViewControll ...
- 使用Vue构建单页应用一
一. 环境准备 1 安装Node.js 最好安装node.js-6.X.X 以上版本,https://nodejs.org/en/ 我使用的是 v6.3.1 Current 版本.Node. ...
- git详细使用教程入门到精通(史上最全的git教程)
Git是分布式版本控制系统,那么它就没有中央服务器的,每个人的电脑就是一个完整的版本库,这样,工作的时候就不 需要联网了,因为版本都是在自己的电脑上.既然每个人的电脑都有一个完整的版本库,那多个人如何 ...
- python 基于udp 连接
#udp 服务端 #导入socket模块 from socket import * #ip加端口 类型为元组 ip_port=('127.0.0.1',8002) #最大接收字节 buffer_siz ...
- 个人免签即时到账收款接口 bufpay.com 支持多账号收款
有很多 bufpay 的用户反馈,单个手机收款有些时候不太方便,切换手机太麻烦:或者是营业额比较多,希望分摊到多个账号上面. 基于以上的问题,bufpay 开发了多手机收款的功能:每个收款的手机安装 ...
- ubuntu14.04安装qt-4.8.4
题记:因为工作中用到qt的qmake工具生成x项目的Makefile文件,因为原有工程用的是4.8.4版本的,因此在此基础之上安装此版本. 用安装包工具进行安装qt不能直接安装到4.8.4版本的,因此 ...