M. Monster Hunter——2020南京

LINK
M. Monster Hunter
time limit per test1 second
memory limit per test256 megabytes
inputstandard input
outputstandard output
There is a rooted tree with n vertices and the root vertex is 1. In each vertex, there is a monster. The hit points of the monster in the i-th vertex is hpi.

Kotori would like to kill all the monsters. The monster in the i-th vertex could be killed if the monster in the direct parent of the i-th vertex has been killed. The power needed to kill the i-th monster is the sum of hpi and the hit points of all other living monsters who lives in a vertex j whose direct parent is i. Formally, the power equals to
hpi+∑the monster in vertex j is \bf{alive}and i is the direct parent of jhpj
In addition, Kotori can use some magic spells. If she uses one magic spell, she can kill any monster using 0 power without any restriction. That is, she can choose a monster even if the monster in the direct parent is alive.

For each m=0,1,2,⋯,n, Kotori would like to know, respectively, the minimum total power needed to kill all the monsters if she can use m magic spells.

Input
There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:

The first line contains an integer n (2≤n≤2×103), indicating the number of vertices.

The second line contains (n−1) integers p2,p3,⋯,pn (1≤pi<i), where pi means the direct parent of vertex i.

The third line contains n integers hp1,hp2,⋯,hpn (1≤hpi≤109) indicating the hit points of each monster.

It’s guaranteed that the sum of n of all test cases will not exceed 2×103.

Output
For each test case output one line containing (n+1) integers a0,a1,⋯,an separated by a space, where am indicates the minimum total power needed to kill all the monsters if Kotori can use m magic spells.

Please, DO NOT output extra spaces at the end of each line, otherwise your answer may be considered incorrect!

Example
inputCopy
3
5
1 2 3 4
1 2 3 4 5
9
1 2 3 4 3 4 6 6
8 4 9 4 4 5 2 4 1
12
1 2 2 4 5 3 4 3 8 10 11
9 1 3 5 10 10 7 3 7 9 4 9
outputCopy
29 16 9 4 1 0
74 47 35 25 15 11 7 3 1 0
145 115 93 73 55 42 32 22 14 8 4 1 0

树形背包:
关键是一条一条子树加起来就可以了。
子树先递归到各层封装好,确保可以有一个整体性质,最后根节点的整体性质就是整个树的整体性质

一般的背包,是每个容量是一个性质,更新性质,取最大容量的性质。

这就是区别了,树状是各节点一个性质,更新性质,取根节点的性质

//写递归要有一定的整体感,不然很难套起来
//——每棵子树删哪个其实不重要,重要的是一个子树里删了几个,可从这入手
//——从大到小看,因为是用原来的小的更新现阶段的大的。用小更新大,从大算到小保留原来的小,反之若用大更新小,就从小到大保留大 

/*//f[i][j][0/1]表示i子树内花费魔力后留下j个(原文有误)的最小代价,此时i是否被删掉
	siz[u] = 1;//规模 
	f[u][0][0] = 0; f[u][1][1] = w[u];
	for( auto v:vec[u] )
	{
		dfs( v );
		for(int j=siz[u];j>=0;j--)//u子树内选多少物品 
		for(int q=siz[v];q>=0;q--)//v子树内选多少个物品 
		{
			f[u][j+q][0] = min( f[u][j+q][0],f[u][j][0]+min( f[v][q][0],f[v][q][1] ) );
			f[u][j+q][1] = min( f[u][j+q][1],f[u][j][1]+min( f[v][q][0],f[v][q][1]+w[v] ) );
		}
	 	siz[u] += siz[v];
	}
	*/

/*
	状态设计:
	d p [ 0 ] [ i ] [ j ] dp[0][i][j]dp[0][i][j] 表示删掉结点i,并且子树中留下j个结点的价值。
	d p [ 1 ] [ i ] [ j ] dp[1][i][j]dp[1][i][j] 表示留下结点i,并且子树中留下j-1个结点的价值。
	状态转移:
	d p [ 0 ] [ i ] [ j + k ] = m i n ( f [ 0 ] [ i ] [ j + k ] , f [ 0 ] [ i ] [ j ] + m i n ( f [ 0 ] [ s o n ] [ k ] , f [ 1 ] [ s o n ] [ k ] ) ) dp[0][i][j+k] = min(f[0][i][j+k],f[0][i][j]+min(f[0][son][k],f[1][son][k]))
	dp[0][i][j+k]=min(f[0][i][j+k],f[0][i][j]+min(f[0][son][k],f[1][son][k]))
	
	d p [ 1 ] [ i ] [ j + k ] = m i n ( f [ 1 ] [ i ] [ j + k ] , f [ 1 ] [ i ] [ j ] + m i n ( f [ 0 ] [ s o n ] [ k ] , f [ 1 ] [ s o n ] [ k ] + a [ s o n ] ) ) dp[1][i][j+k] = min(f[1][i][j+k],f[1][i][j]+min(f[0][son][k],f[1][son][k] + a[son]))
	dp[1][i][j+k]=min(f[1][i][j+k],f[1][i][j]+min(f[0][son][k],f[1][son][k]+a[son]))
	
	其中a数组存储的是结点的价值
	*/

代码:

#pragma GCC optimize(3,"Ofast","inline")
//#pragma GCC optimize(2)
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#include<iostream>
#include<algorithm>
#include<bitset>
//#include<string>
//#include<sstream>
#include<vector>
#include<map>
//#include<set>
//#include<ctype.h>
//#include<stack>
//#include<queue>
#ifdef LOCAL
FILE*FP=freopen("text.in","r",stdin);
//FILE*fp=freopen("text.out","w",stdout);
#endif
using namespace std;
#define ll long long
#define ld long double
#define pii pair<int,int>
#define piii pair<int,pii>
#define pll pair<ll,ll>
#define plll pair<ll,pll> 
#define pdd pair<double,double>
#define pdi pair<double,int>
#define pid pair<int,double>
#define vi vector <int> 
#define vii vector <vi> 
#define vl vector<ll>
#define st first
#define nd second
#define pb push_back
#define mp make_pair
#define mem(a,b) memset(a,b,sizeof(a))
#define _mem(a,b,c) memset(a,b,sizeof(a[0])*c)
#define _forplus(i,a,b) for( register int i=(a); i<=(b); i++)
#define forplus(i,a,b) for( register int i=(a); i<(b); i++)
#define _forsub(i,a,b) for( register int i=(a); i>=(b); i--)
#define _forauto(a,b) for(auto &(a):(b))
#define _forautome(a,b,c) for(auto (a) = (b); (a) != (c); (a)++)
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
//参考:set<int>::iterator iter = vis.begin();
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
#define pi (acos(-1))
#define EPS 0.00000001
#define MOD 1000000007
#define fastio 	std::ios::sync_with_stdio(false);std::cin.tie(0);
#define int ll
#define N 2005
vi vec[N];
int t,n,te;
int w[N],siz[N],f[N][N][2];//价值;规模;序号,用魔力删除个数,自身是0否1用魔力删除 的花费 
void dfs(int u){
	siz[u]=1;
	f[u][0][0] = 0; f[u][1][1] = w[u];//被删完,留这一个 
	//f[u][0][1]=w[u];
	//f[u][1][0]=0;
	_forauto(v,vec[u]){
		dfs(v);
		_forsub(j,siz[u],0){//未计算到这个子树时,留下j个  
			_forsub(k,siz[v],0){//这个子树,留下k个 
				f[u][j+k][0]=min(f[u][j+k][0],f[u][j][0]+min(f[v][k][0],f[v][k][1]));//不留自己 
				f[u][j+k][1]=min(f[u][j+k][1],f[u][j][1]+min(f[v][k][0],f[v][k][1]+w[v]));//留着自己,要考虑子代 
				
			}
		}
		siz[u]+=siz[v];
	}
	
}
int32_t main(){
	fastio
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		_forplus(i,2,n){
			scanf("%d",&te);
			vec[te].pb(i);
		}
		_forplus(i,1,n){
			scanf("%d",&w[i]);
		}
		//mem(f,0x3f);//注意!容易超时 
		_forplus(i,0,n){
			_forplus(j,0,n){
				f[i][j][0]=f[i][j][1]=LINF;
			}
		}
		dfs(1);
		for(int i=n;i>=0;i--)//留下i个 
			cout << min( f[1][i][0],f[1][i][1] ) << " ",vec[i].clear();
		cout << endl;
	}
	return 0;
}

鸣谢:
https://codeforces.com/gym/102992/status?pageIndex=3&order=BY_PROGRAM_LENGTH_ASC

https://www.cnblogs.com/hznumqf/p/14295079.html

https://blog.csdn.net/NeverMakeIt/article/details/112062775

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值