Jzoj5426 摘Galo

来源:互联网 发布:软件架构是什么 编辑:程序博客网 时间:2024/05/21 00:16

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);}