Jzoj5426 摘Galo

0v0在野外看到了一棵Galo树,看到食物的0v0瞪大了眼睛,变成了OvO。
这棵Galo树可以看做是一棵以1号点为根的n个点的有根数,除了根节点以外,每个节点i都有一个Galo,美味度为w[i]。
OvO发现,如果她摘下了i号Galo,那么i的子树中的Galo以及i到根的路径上的其他Galo都会死掉。
OvO的袋子只能装k个Galo,她的嘴巴里还能叼1个,请问她所摘Galo的美味度之和的最大值是多少?

基本树形背包,设f[i][j]表示以i为根的子树,选了j个galo的最大美味度

那么,f[i][j]=max(f[i][k],f[v][j-k]),f[i][1]=max(f[i][1],w[i])

注意两个优化

1.题目中的n,k限制比较诡异:n*k<=10^7

于是这道题f不能直接开要开成一维的,访问f[i][j]要变成f[ik+j]

2.树形背包直接做是会T的,要么跑出dfs序变成序列做法,或者是减少背包状态

我们令size'[x]表示以x为根的叶子结点个数

那么显然多过size'[x]的部分是可以不用的,这样就可以防止别人用几条链的数据来卡你

#include<vector>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 100010
#define f(i,j) F[inv[i]+j-1]
using namespace std;
vector<int> s[N];
int n,_k,val[N]={0},sz[N],inv[N];
long long F[N*200];
int dfs(int x){
	sz[x]=0;
	if(s[x].size()==0){
		f(x,0)=0;
		f(x,1)=val[x];
		return ++sz[x];
	}
	for(int v,i=0,z=s[x].size();i<z;++i){
		sz[x]+=dfs(v=s[x][i]);
		for(int j=min(_k,sz[x]);j;--j)
			for(int k=min(j,sz[v]);k;--k)
				f(x,j)=max(f(x,j),f(x,j-k)+f(v,k));
	}
	f(x,1)=max(f(x,1),(long long)val[x]);
	return sz[x];
}
int main(){
	freopen("galo.in","r",stdin);
	freopen("galo.out","w",stdout);
	scanf("%d%d",&n,&_k); ++_k; _k=min(n,_k);
	for(int i=1;i<=n;++i) inv[i]=(_k+1)*i;
	for(int x,y,i=2;i<=n;++i){
		scanf("%d%d",&x,val+i);
		s[x].push_back(i); 
	}
	dfs(1);
	for(int i=1;i<=_k;++i) F[0]=max(F[0],f(1,i));
	printf("%lld
",*F);
}

原文地址:https://www.cnblogs.com/Extended-Ash/p/7846033.html