题目传送门
分析:
看到这个限制条件构成了一个树形结构,相当于儿子节点的生产数一定不小于父亲节点,且不比父亲节点多超过(D)个
差分一下,如果一个节点要制造物品,那么其子树内的节点全部都制造一个物品,非根节点最多造(D)个物品
效果相同,并且将每个节点的代价和收益变为子树和,我们就可以脱离树形结构直接多重背包了
但是(Dleq 10^9)不能够枚举
我们回到最开始学动规背包时,相想必大家在解决背包问题时一定想过一个贪心:
一个物品重量为(w),价值为(v),我们把物品按(frac{v}{w})从大到小排序,贪心加入
这样做会在当背包剩余空间不够大的时候出现问题,相信大家也把自己驳倒过
但是在背包空间足够大时,这样的做法就没有问题
我们可以在背包空间不够大的时候做多重背包,剩余的体积用于贪心加入物品
假设有两个物品(i,j)满足(frac{v_i}{w_i}>frac{v_j}{w_j}),那么在收益相等(选了(v_j)个(i),选了(v_i)个(j))的情况下,选(v_j)个(i)显然会更优
从而在比(frac{v_i}{w_i})大的物品还能加的时候,比(frac{v_i}{w_i})小的至多会选(v_i-1)个
我们这道题一个点的(v)为子树和,最大为(n)
相当于做背包的每种物品最多有(min(n,D))个,背包体积最大为(n^3)
使用二进制分组优化一下,复杂度可以接受
剩下的暴力加入就好了
总复杂度(O(n^4logX))
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#include<queue>
#include<bitset>
#include<map>
#include<set>
#define maxn 55
#define maxm 125005
#define INF 0x3f3f3f3f
#define MOD 1000000007
using namespace std;
inline int getint()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
return num*flag;
}
int n,X,d;
int fa[maxn];
struct node{
long long v;
int w,id;
}a[maxn];
inline bool cmp(node a,node b){return a.w*b.v>b.w*a.v;}
long long dp[maxm];
int main()
{
n=getint(),X=getint(),d=getint();
a[1].v=getint();
for(int i=2;i<=n;i++)a[i].v=getint(),fa[i]=getint();
for(int i=1;i<=n;i++)a[i].id=i,a[i].w=1;
for(int i=n;i>1;i--)a[fa[i]].v+=a[i].v,a[fa[i]].w+=a[i].w;
int L=min(n,d);
memset(dp,INF,sizeof dp),dp[0]=0;
for(int i=1;i<=n;i++)
{
int x=L;
for(int j=0;(1<<j)<=x;j++)
{
int w=a[i].w<<j;
long long v=a[i].v<<j;
for(int k=maxm-1;k>=w;k--)dp[k]=min(dp[k],dp[k-w]+v);
x-=1<<j;
}
if(x)
{
int w=a[i].w*x;
long long v=a[i].v*x;
for(int j=maxm-1;j>=w;j--)dp[j]=min(dp[j],dp[j-w]+v);
}
}
sort(a+1,a+n+1,cmp);
while(a[n].id!=1)n--;
long long ans=0;
for(int i=0;i<maxm;i++)
{
if(dp[i]>X)continue;
long long w=i,v=dp[i];
for(int j=1;j<n;j++)
{
long long c=min(1ll*d-L,(X-v)/a[j].v);
w+=c*a[j].w,v+=c*a[j].v;
}
long long c=(X-v)/a[n].v;
w+=c*a[n].w,v+=c*a[n].v;
ans=max(ans,w);
}
printf("%lld
",ans);
}