• 我们可以通过 dfn 和后序 dfn 刻画一条链以及其两边的限制。

我们可以免费选择一条链上所有点的一个苹果。

考虑这个链将这个树分成三块,链,左,右。链左边可以通过 dfs 背包来实现,右边可以用后序 dfn 进行 dp(具体见P6326),然后枚举叶子,合并三个块 \(O(nk^2)\)

考虑将链上的背包干掉,如果我们用 dfs 扫 \(u\) 链前左边的背包的时候不要求必须选择 \(u\) 到根节点链上的苹果,同时将 \(i\) 到根节点上的苹果数量变成 \(a_i-1\) 个,回溯的时候再加上最后那一个东西即可。

代码很易懂,可以看看。

#include<bits/stdc++.h>
using namespace std;
#define N 20005
#define V 500005
#define inf 0x3f3f3f3f
int t,n,m,tot,val[N],ans;
vector<int> G[N],f[N],g[N];
int w[N],c[N],dad[N],dfn[N],rnk[N],siz[N],leaf[N],vis[N],q[V];
void dfs(int u){
	siz[u]=leaf[u]=1;val[u]+=w[u];
	for(int v:G[u])if(v^dad[u])val[v]=val[u],dfs(v),siz[u]+=siz[v],leaf[u]=0;
	rnk[dfn[u]=++tot]=u;
}
void dfs2(int u){
	vis[u]=1;
	if(leaf[u]){
		for(int k=0;k<=m;k++)ans=max(ans,g[u][k]+f[dfn[u]-1][m-k]+val[u]);
	}
	reverse(G[u].begin(),G[u].end());
	for(int v:G[u])if(v^dad[u]){
		int l=0,r=-1;
		for(int j=0;j<=m;j++){
			while(l<=r&&j-q[l]>c[v]-1)++l;
			while(l<=r&&g[u][q[r]]+(j-q[r])*w[v]<g[u][j])--r;q[++r]=j;
			if(l<=r)g[v][j]=max(g[v][j],g[u][q[l]]+(j-q[l])*w[v]);
		}
		dfs2(v);
		for(int j=1;j<=m;j++)g[u][j]=max(g[v][j-1]+w[v],g[u][j]);
	}
}
void work(){
	for(int i=0;i<=n;i++)G[i].clear(),vector<int>().swap(f[i]),vector<int>().swap(g[i]),val[i]=0;ans=-inf;tot=0;
	scanf("%d%d",&n,&m);
	for(int i=0;i<=n;i++)f[i].resize(m+1,-inf),g[i].resize(m+1,-inf);
	for(int u=1;u<=n;u++)scanf("%d%d%d",&dad[u],&c[u],&w[u]),G[dad[u]].push_back(u);
	dfs(1);
	f[0][0]=0;
	for(int i=1;i<=n;i++){
		int u=rnk[i];int l=0,r=-1;
		for(int j=0;j<=m;j++)f[i][j]=max(f[i][j],f[i-siz[u]][j]);
		for(int j=0;j<=m;j++){
			while(l<=r&&j-q[l]>c[u])++l;
			if(l<=r)f[i][j]=max(f[i-1][q[l]]+(j-q[l])*w[u],f[i][j]);
			while(l<=r&&f[i-1][q[r]]+(j-q[r])*w[u]<f[i-1][j])--r;
			q[++r]=j;
		}
	}
	for(int i=0;i<=n;i++)for(int j=1;j<=m;j++)f[i][j]=max(f[i][j],f[i][j-1]);
	for(int i=0;i<c[1];i++)g[1][i]=w[1]*i;
	dfs2(1);
	printf("%d\n",ans);
}
int main(){
	scanf("%d",&t);
	while(t--)work();
}