[2018.07.26 T2] 背包问题

16 篇文章 0 订阅
1 篇文章 0 订阅

暂无链接

背包问题

【问题描述】

给你一棵树以1号节点为根的树,每个节点上有一个体积为v,价值为w的物品。现在要你统计,对于所有点i,如果只能取子树i中的物品,则容积为m的背包至多能装总价值多少的物品。

【输入格式】

第一行两个整数n,m,分别表示树的节点个数和背包容积。

接下来n行,每行两个整数,分别表示每个点上物品的价值和体积。

接下来一行n-1个整数,分别表示2-n号节点的父节点。保证 fai<i f a i < i

【输出格式】

一行n个整数,依次表示编号为i的子树的答案。

【输入样例】

13 6
4 7699
2 8393
4 3023
6 4775
1 998
3 7194
5 8194
1 7912
4 1874
6 5298
5 359
6 6171
1 9627
1 1 2 4 4 4 3 1 9 6 1 12

【输出样例】

26930 16585 10935 9192 998 7194 8194 7912 5298 5298 359 9627 9627

【数据范围】

对于30%的数据:n,m ≤ 500

对于60%的数据:fa[i]在[1,i)间等概率随机生成。

对于100%的数据:n ≤ 30000,m ≤ 500。

题解

本题区分了考试前一天学三十二叉堆和学 dsu on tree d s u   o n   t r e e 的同学,而我就是那个去学三十二叉堆的。

不过 O(n2m) O ( n 2 m ) 暴力背包居然有 60 60 分,感谢张瀚文大爷不杀之恩orz。。。

感觉 dsu on tree d s u   o n   t r e e 非常暴力,先递归处理轻儿子做背包,再递归做重儿子,然后增量式的插入根以及其他儿子的信息。

放在这道题上,就是先递归做轻儿子的背包,求出轻儿子的答案,然后递归下去做重儿子的,等重链做完了再遍历一遍轻儿子暴力插入重链得到的背包。

复杂度证明和树剖类似, dsu on tree d s u   o n   t r e e 相当于采用了重链剖分方式,这样暴力插入的次数不超过 O(nlog2n) O ( n l o g 2 n ) 次,总复杂度为 O(nmlog2n) O ( n m l o g 2 n )

代码

顺便学习一波 C C ++11的姿势

#include<bits/stdc++.h>
using namespace std;
const int M=3e4+5;
int cap[M],val[M],siz[M],son[M],ans[M],dp[505],n,m,a;
vector<int>mmp[M];
void dfs(int v){siz[v]=1;for(int to:mmp[v]){dfs(to);siz[v]+=siz[to];if(siz[to]>siz[son[v]])son[v]=to;}}
void ins(int v,int w){for(int i=m-v;i>=0;--i)dp[i+v]=max(dp[i+v],dp[i]+w);}
void ins(int v){ins(cap[v],val[v]);for(int to:mmp[v])ins(to);}
void dsu(int v,int p)
{
    for(int to:mmp[v])if(to!=son[v])dsu(to,0);if(son[v])dsu(son[v],1);ins(cap[v],val[v]);
    for(int to:mmp[v])if(to!=son[v])ins(to);ans[v]=dp[m];if(!p)memset(dp,0,sizeof(dp));
}
void in()
{
    scanf("%d%d",&n,&m);for(int i=1;i<=n;++i)scanf("%d%d",&cap[i],&val[i]);
    for(int i=2;i<=n;++i)scanf("%d",&a),mmp[a].push_back(i);
}
void ac(){dfs(1);dsu(1,0);for(int i=1;i<=n;++i)printf("%d ",ans[i]);}
int main(){in(),ac();}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值