正题
题目链接:https://www.luogu.com.cn/problem/AT4120
题目大意
给出nnn个物品和一个容量mmm,第iii个物品体积为cic_ici。除了第一个物品每个物品还有一个pi(pi<i)p_i(p_i<i)pi(pi<i)表示如果pip_ipi个物品选择了xxx个,第iii个物品选择了yyy个要求满足x≤y≤x+dx\leq y\leq x+dx≤y≤x+d。
1≤n≤50,1≤m,ci≤109,0≤d≤109,1≤p<i1\leq n\leq 50,1\leq m,c_i\leq 10^9,0\leq d\leq 10^9,1\leq p<i1≤n≤50,1≤m,ci≤109,0≤d≤109,1≤p<i
解题思路
一个简单的转换,变成一棵树之后选择就变为了一次必须选择一个子树。除了点111外其他最多就只能选择ddd次。
就变成了一个多重背包,但是容量和体积很大,考虑贪心。因为价值都比较小,对于足够大的情况性价比贪心是对的,所以我们可以考虑先把小的直接做背包,大的贪心选。
背包时每个物品最多选min{n,d}min\{n,d\}min{n,d}次,设fif_ifi表示价值为iii时的最小体积,然后枚举一个背包权值,剩下的贪心就好了。二进制分组或者单调队列优化多重背包都能过
时间复杂度O(n4logn)O(n^4\log n)O(n4logn)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=51;
struct node{ll w,v,id;
}a[N];
ll n,m,d,p[N],f[N*N*N],ans;
bool cmp(node x,node y)
{return x.v*y.w>y.v*x.w;}
signed main()
{scanf("%lld%lld%lld",&n,&m,&d);scanf("%lld",&a[1].w);a[1].v=1;a[1].id=1;for(ll i=2;i<=n;i++)scanf("%lld%lld",&a[i].w,&p[i]),a[i].v=1;for(ll i=n;i>1;i--)a[p[i]].w+=a[i].w,a[p[i]].v+=a[i].v,a[i].id=i;ll mx=n*n*n;memset(f,0x3f,sizeof(f));f[0]=0;for(ll i=1;i<=n;i++){ll x=min(n,d);for(ll j=1;j<=x;j<<=1){ll w=a[i].w*j,v=a[i].v*j;for(ll k=mx;k>=v;k--)f[k]=min(f[k],f[k-v]+w); x-=j;}if(!x)continue;ll w=a[i].w*x,v=a[i].v*x;for(ll k=mx;k>=v;k--)f[k]=min(f[k],f[k-v]+w);}sort(a+1,a+1+n,cmp);for(ll p=0;p<=mx;p++){if(f[p]>m)break;ll v=p,w=f[p],i=1;for(i=1;i<n;i++){if(a[i].id==1)break;ll x=min(d-min(n,d),(m-w)/a[i].w);w+=x*a[i].w;v+=x*a[i].v;}ll x=(m-w)/a[i].w;w+=x*a[i].w;v+=x*a[i].v;ans=max(ans,v); }printf("%lld\n",ans);return 0;
}