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;
}

posted on 2020-04-22 17:30  autoint  阅读(287)  评论(0编辑  收藏  举报

导航