列表

详情


NC17620. [NOI2009]二叉查找树

描述

已知一棵特殊的二叉查找树。根据定义,该二叉查找树中每个结点的数据值都比它左子树结点的数据值大,而比它右子树结点的数据值小。
另一方面,这棵查找树中每个结点都有一个权值,每个结点的权值都比它的儿子结点的权值要小。
已知树中所有结点的数据值各不相同;所有结点的权值也各不相同。这时可得出这样一个有趣的结论:如果能够确定树中每个结点的数据值权值,那么树的形态便可以唯一确定。因为这样的一棵树可以看成是按照权值从小到大顺序插入结点所得到的、按照数据值排序的二叉查找树。
一个结点在树中的深度定义为它到树根的距离加1。因此树的根结点的深度为1
每个结点除了数据值权值以外,还有一个访问频度。我们定义一个结点在树中的访问代价为它的访问频度乘以它在树中的深度整棵树的访问代价定义为所有结点在树中的访问代价之和。
       现在给定每个结点的数据值权值访问频度,你可以根据需要修改某些结点的权值,但每次修改你会付出K额外修改代价。你可以把结点的权值改为任何实数,但是修改后所有结点的权值必须仍保持互不相同。现在你要解决的问题是,整棵树的访问代价额外修改代价的和最小是多少?

输入描述

第一行包含两个正整数N和K。N为结点的个数,K为每次修改所需的额外修改代价
接下来一行包含N个非负整数,是每个结点的数据值
再接下来一行包含N个非负整数,是每个结点的权值
再接下来一行包含N个非负整数,是每个结点的访问频度
所有的数据值、权值、访问频度均不超过400000。每两个数之间都有一个空格分隔,且行尾没有空格。

输出描述

只有一个数字,即你所能得到的整棵树的访问代价额外修改代价之和的最小值。

示例1

输入:

4 10
1 2 3 4
1 2 3 4
1 2 3 4

输出:

29

说明:

输入的原图是左图,它的访问代价是1×1+2×2+3×3+4×4=30。最佳的修改方案是把输入中的第3个结点的权值改成0,得到右图,访问代价是1×2+2×3+3×1+4×2=19,加上额外修改代价10,一共是29。

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++14(g++5.4) 解法, 执行用时: 69ms, 内存消耗: 4296K, 提交时间: 2019-11-30 20:05:18

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rint register int
#define min(a,b) (a<b? a:b)
using namespace std;
struct node{int x,y,z;}a[100];
int f[100][100][100];
int n,K;
inline int read()
{
    int w=1,ans=0;
    char ch=getchar();
    while(!isdigit(ch)) {if (ch=='-') w=-1; ch=getchar();}
    while(isdigit(ch)) {ans=(ans<<3)+(ans<<1)+(ch^48); ch=getchar();}
    return ans*w;
}
inline bool cmp(node a,node b)
{
    return a.y<b.y;
}
inline bool cmp2(node a,node b)
{
    return a.x<b.x;
}
int main()
{
    //freopen("tree.in","r",stdin);
    //freopen("tree.out","w",stdout);
    n=read();K=read();
    for (int i=1;i<=n;i++) a[i].x=read();//数据
    for (int i=1;i<=n;i++) a[i].y=read();//权值
    for (int i=1;i<=n;i++) a[i].z=read();//访问频度
    sort(a+1,a+n+1,cmp);//按权值排序,离散化
    for (int i=1;i<=n;i++) a[i].y=i;//将权值离散化
    sort(a+1,a+n+1,cmp2);
    for (int i=1;i<=n;i++) a[i].z+=a[i-1].z;//前缀和
    memset(f,0x7f,sizeof f);
    for (int i=1;i<=n+1;i++)
     for (int w=0;w<=n;w++)
      f[i][i-1][w]=0;
       
    for (rint w=n;~w;w--)//~w指w非零,区间内所有权值大于w
     for (rint i=n;i;i--)
      for (rint j=i;j<=n;j++)
       for (rint k=i;k<=j;k++)//区间dp枚举根节点位置
       {
         if (a[k].y>=w)
          f[i][j][w]=min(f[i][j][w],f[i][k-1][a[k].y]+f[k+1][j][a[k].y]+a[j].z-a[i-1].z);
          //a[j].z-a[i-1].z:当树深加1,把每个访问频度都再加一次
         f[i][j][w]=min(f[i][j][w],f[i][k-1][w]+f[k+1][j][w]+a[j].z-a[i-1].z+K);
       }
    printf("%d",f[1][n][1]);
    return 0;   
}

C++(clang++ 11.0.1) 解法, 执行用时: 25ms, 内存消耗: 4324K, 提交时间: 2022-10-02 18:27:51

#include<bits/stdc++.h>
#define min(a,b) (a<b? a:b)
using namespace std;
struct node{int x,y,z;}a[100];
int f[100][100][100],n,K;
inline int read(){
    int w=1,ans=0;
    char ch=getchar();
    while(!isdigit(ch)) {if (ch=='-') w=-1; ch=getchar();}
    while(isdigit(ch)) {ans=(ans<<3)+(ans<<1)+(ch^48); ch=getchar();}
    return ans*w;
}
inline bool cmp(node a,node b){return a.y<b.y;}
inline bool cmp2(node a,node b){return a.x<b.x;}
int main(){
    n=read(),K=read();
    for (int i=1;i<=n;i++) a[i].x=read();
    for (int i=1;i<=n;i++) a[i].y=read();
    for (int i=1;i<=n;i++) a[i].z=read();
    sort(a+1,a+n+1,cmp);
    for (int i=1;i<=n;i++) a[i].y=i;
    sort(a+1,a+n+1,cmp2);
    for (int i=1;i<=n;i++) a[i].z+=a[i-1].z;
    memset(f,0x7f,sizeof f);
    for (int i=1;i<=n+1;i++)for (int w=0;w<=n;w++)f[i][i-1][w]=0;
    for (int w=n;~w;w--)
        for (int i=n;i;i--)
            for (int j=i;j<=n;j++)
                for (int k=i;k<=j;k++){
                    if (a[k].y>=w)f[i][j][w]=min(f[i][j][w],f[i][k-1][a[k].y]+f[k+1][j][a[k].y]+a[j].z-a[i-1].z);
                    f[i][j][w]=min(f[i][j][w],f[i][k-1][w]+f[k+1][j][w]+a[j].z-a[i-1].z+K);
                }
    printf("%d",f[1][n][1]);
    return 0;   
}

上一题