[2018.07.26 T2] 背包问题

暂无链接

背包问题

【问题描述】

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

【输入格式】

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

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

接下来一行n-1个整数,分别表示2-n号节点的父节点。保证 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。

题解

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

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

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

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

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

代码

顺便学习一波 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();}

猜你喜欢

转载自blog.csdn.net/shadypi/article/details/81224902
T2