------
显然,可以根据优惠券的约束关系构造成一颗树,考虑树形DP
g[u][i]=u为根的子树,购买了i件物品,不能使用优惠卷的最小花销
f[u][i]=u为根的子树,买了i件物品,能使用优惠券,且必定用优惠价买了u物品的最小花销
f′[u][i]=f[u][i+1]−(c[u]−d[u])
g[u][i]=min{g[u][i],g[u][i−j]+g[v][j]} | (0<=j<=i,v为u的儿子节点)
f′[u][i]=min{f′[u][i],f′[u][i−j]+min(g[v][j],f[v][j])} | (0<=j<=i,v为u的儿子节点)
f[u][i]=f′[u][i−1]+(c[u]−d[u])
显然,直接DP需要O(n3)
有个比较玄学的优化,维护size[u]=当前u为根的子树已经发现的节点个数
首先对任意u,size[u]=1,求出儿子v后,背包问题搞一下,然后size[u]+=size[v],继续求解其他儿子...
这样复杂度其实只有O(n2),因为子树u内任意2个点对,只同时求了一次
注意如果size[u]+=size[v],再对u,v求背包,复杂度就变为O(n3)了,因为子树v也与自身求了一次背包
#include<stdio.h>
#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define MEM(a,x) memset(a,x,sizeof(a))
#define lowbit(x) ((x)&-(x))
using namespace std;
const int inf=0x3f3f3f3f;
const int N = 5e3+5;
vector<int>G[N];
int c[N],d[N],f[N][N],g[N][N],size[N];
void init(){
MEM(f,0x3f);
MEM(g,0x3f);
MEM(size,0);
for(int i=0;i<N;++i){
G[i].clear();
f[i][0]=g[i][0]=0;
}
}
void dp(int u,int n){
g[u][1]=c[u];
size[u]=1;
for(auto v:G[u]){
dp(v,n);
//size[u]+=size[v]; 放这里O(n^3)
for(int i=size[u];i>=0;--i){
for(int j=size[v];j>=0;--j){
g[u][i+j]=min(g[u][i+j],g[u][i]+g[v][j]);
if(i!=0){
f[u][i+j-1]=min(f[u][i-1+j],f[u][i-1]+min(g[v][j],f[v][j]));
}
}
}
size[u]+=size[v];//O(n^2)
}
for(int i=size[u];i>=1;--i){
f[u][i]=f[u][i-1]+c[u]-d[u];
}
}
int main()
{
//freopen("/home/lu/code/r.txt","r",stdin);
int n,b;
while(~scanf("%d%d",&n,&b)){
init();
scanf("%d%d",&c[0],&d[0]);
for(int i=1;i<n;++i){
int x;
scanf("%d%d%d",&c[i],&d[i],&x);
G[x-1].push_back(i);
}
dp(0,n);
for(int i=n;i>=0;--i){
if(f[0][i]<=b||g[0][i]<=b){
printf("%d\n",i);
break;
}
}
}
return 0;
}