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\n",*F);
}