P1864 [NOI2009]二叉查找树

链接P1864 [NOI2009]二叉查找树
  • 这题还是蛮难的……是我菜
  • 题目描述中的一大堆其实就是在描述\(treap.\),考虑\(treap\)的一些性质:
  • 首先不管怎么转,中序遍历是确定的,所以先按照数据值排序,变成序列问题。
  • 其次是父亲的权值比儿子小,但是这是个相对关系,所以对权值离散。
  • 问题变成了对一个中序构造一棵树使得满足\(treap\)的第二条性质。
  • \(f_{l,r,v}\)表示区间\(l,r\),最小权值大于等于\(v\)的最小代价,这种状态的好处在于我们可以快速知道一段序列的父亲应该最小要多大才可以,因为父亲的权值是一定小于所有儿子的,所以小于等于\(v\)就可以了。
  • 转移首先有:\[ f_{l,r,v}=min(f_{l,p-1,v}+f_{p+1,r,v}+K+P);\]
  • 其中\(K\)是一次修改代价,\(P\)是区间和,\(p\)是枚举的父亲。
  • 这里是强制修改当前权值了。
  • 然后如果\(v_p>=v\),有\[f_{l,r,v}=min(f_{l,p-1,vl}+f_{p+1,r,vl}+P);\]
  • 其中\(vl\)\(p\)点权值。
  • 考虑一下边界即可,注意如果\(l>r\)\(f_{l,r,v}=0\)
#include<bits/stdc++.h>
#define R register int
#define ll long long 
using namespace std;
const int N=100;
int n,len,K,O[N];
ll num[N],f[N][N][N],ans;
struct Qs{int id,vl,pd;}w[N];
int cmpid(const Qs &x,const Qs &y){return x.id<y.id;}
int gi(){
    R x=0,k=1;char c=getchar();
    while((c<'0'||c>'9')&&c!='-')c=getchar();
    if(c=='-')k=-1,c=getchar();
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
    return x*k;
}
ll Get(R l,R r,R v){
    if(l>r)return 0;
    if(f[l][r][v]!=-1)return f[l][r][v];
    f[l][r][v]=1e18;ll P=num[r]-num[l-1];
    if(l==r){f[l][r][v]=P+((w[l].vl>=v)?0:K);return f[l][r][v];}
    for(R p=l;p<=r;++p){
        f[l][r][v]=min(f[l][r][v],Get(l,p-1,v)+Get(p+1,r,v)+K+P);
        if(w[p].vl>=v)
            f[l][r][v]=min(f[l][r][v],Get(l,p-1,w[p].vl)+Get(p+1,r,w[p].vl)+P);
    }
    return f[l][r][v];
}
int main(){
    freopen("s.in","r",stdin);
    n=gi(),K=gi(),ans=1e18;
    for(R i=1;i<=n;++i)w[i].id=gi();
    for(R i=1;i<=n;++i)
        w[i].vl=gi(),O[++len]=w[i].vl;
    for(R i=1;i<=n;++i)w[i].pd=gi();
    sort(w+1,w+n+1,cmpid);
    sort(O+1,O+len+1),len=unique(O+1,O+len+1)-O-1;
    for(R i=1;i<=n;++i){
        w[i].vl=lower_bound(O+1,O+len+1,w[i].vl)-O;
        num[i]=num[i-1]+w[i].pd;
    }
    memset(f,-1,sizeof(f));
    for(R i=1;i<=len;++i)ans=min(ans,Get(1,n,i));
    printf("%lld\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/Tyher/p/9838788.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值