ARC096F Sweet Alchemy
Sweet Alchemy
给定 𝑛 种食物,食物之间有树形的依赖关系。你有 𝑋 个原材料,第 𝑖 种食物每制作一个需要消耗 𝑚𝑖 原材料。你需要制作每种食物若干个,使得总数最大,且需要满足若节点 𝑖 依赖 𝑝𝑖,则 𝑐𝑝𝑖 ≤ 𝑐𝑖 ≤ 𝑐𝑝𝑖 + 𝐷,其中 𝐷 给定,𝑐𝑖 表示制作食物 𝑖 的数量。
𝑛 ≤ 50,𝑋,𝐷, 𝑚𝑖 ≤ 109。
题解
经典差分模型。这么考虑,每当做了一个第 𝑖 种食物,那么把它的子树里的所有食物也各做一个。
那么题目转化为一个背包模型,每种物品有重量 𝑤𝑖,价值 𝑣𝑖(不超过 50),限制选取个数。物品种数不超过 50 个。
如果存在两个物品 𝑖,𝑗,且 \(\frac{𝑣_𝑖}{𝑤_𝑖}≥\frac{𝑣_𝑗}{𝑤_𝑗}\),且 𝑖 还能再选 𝑣𝑗 个,𝑗 已经选了 𝑣𝑖 个,那么减少 𝑣𝑖 个 𝑗 物品,加入 𝑣𝑗 个 𝑖 物品肯定不会更劣。也即是说,大范围我们只需要考虑贪心选取性价比最高的。
由于价值不超过 50,所以把每种物品拿 50 个出来做个多重背包(以价值为状态,即获得 𝑉 的价值最少要多少重量),其余部分一定是按 \(\frac{𝑣_𝑖}{𝑤_𝑖}\) 从大往小排序贪心选择。
DP状态是 \(O(n^{3})\) 的,转移时对于每个物品,DP值按照 \(\bmod v_i\) 分组,每组间用单调队列维护最优转移点,一次转移可以做到 \(O(n^{3})\),总复杂度 \(O(n^{4})\)。
array<int,2> operator+(CO array<int,2>&a,CO array<int,2>&b){
return {a[0]+b[0],a[1]+b[1]};
}
CO int N=51,M=N*N*N,inf=1e18;
int fa[N];
array<int,2> a[N];
int F[M],pos[M];
array<int,2> que[M];
signed main(){
int n=read<int>(),X=read<int>(),D=read<int>();
a[1]={1,read<int>()};
for(int i=2;i<=n;++i) a[i]={1,read<int>()},read(fa[i]);
for(int i=n;i>=2;--i) a[fa[i]]=a[fa[i]]+a[i];
sort(a+1,a+n+1,[&](CO array<int,2>&a,CO array<int,2>&b)->bool{
return a[0]*b[1]>b[0]*a[1];
});
F[0]=0,fill(F+1,F+M+1,inf);
int l=min(D,n),s=0;
for(int i=1;i<=n;++i){
s+=n*a[i][0];
for(int r=0;r<a[i][0];++r){ // remainder
int tot=0;
for(int j=r;j<=s;j+=a[i][0]) pos[++tot]=j;
int L=1,R=0;
for(int j=tot,k=tot;j>=1;--j){
while(L<=R and que[L][0]>j) ++L;
for(;k>=1 and k>=j-l;--k){
array<int,2> x={k,F[pos[k]]-a[i][1]*k};
while(L<=R and que[R][1]>=x[1]) --R;
que[++R]=x;
}
F[pos[j]]=min(F[pos[j]],que[L][1]+a[i][1]*j);
}
}
}
int ans=0;
for(int i=0;i<=s;++i)if(F[i]<=X){
int sum=X-F[i],num=i;
for(int j=1;j<=n;++j){
int u=sum/a[j][1];
if(a[j][0]<n) u=min(u,D-l); // not root
sum-=a[j][1]*u,num+=a[j][0]*u;
}
ans=max(ans,num);
}
printf("%lld\n",ans);
return 0;
}
静渊以有谋,疏通而知事。