arc096_f Sweet Alchemy

arc096_f Sweet Alchemy

https://atcoder.jp/contests/arc096/tasks/arc096_d

UwTa8I.png

Tutorial

这是一个树的结构,可以差分一下,令\(c'_i=c_i-c_{p_i}\),特别的\(c'_1=c_1\),那么限制变为了\(\forall i \in [2,N],c_i \le D\),而\(c_i\)的意义为将子树中的所有甜甜圈制作\(c_i\)次.

那么现在问题可以描述为有\(N\)个物品,每个物品有\(D_i\)个,价值为\(Y_i\),体积为\(X_i\),问大小为\(X\)的背包的最大价值,不必装满.其中除了\(N,Y_i\)之外都很大.

首先,可以贪心的考虑,将物品按单位价值\(\dfrac {Y_i}{X_i}\)排序,但是显然,背包问题贪心是不优的.

但是发现,如果对于排序后的两个物品\(p<q\),若\(p\)有至少\(N\)个没选,\(q\)选择了至少\(N\)个.那么从背包中拿掉\(Y_p\)\(q\)物品,加入\(Y_q\)\(p\)物品,此时价值不变,体积不会更劣.

所以可以从每种物品中拿出\(\min(D_i,N)\)个物品,对这些物品做一次以价值为基准的多重背包,然后枚举价值,对于剩下的物品贪心求解即可.

第一个背包中的价值最大是\(N^3\),所以总复杂度\(O(N^4 \log N)\)

Code

#include <algorithm>
#include <cstdio>
#include <iostream>
#define debug(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
inline char gc() {
	return getchar();
	static char buf[100000],*l=buf,*r=buf;
	return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
}
template<class T> void rd(T &x) {
	x=0; int f=1,ch=gc();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
	while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=gc();}
	x*=f;
}
template<class T> inline bool Cmax(T &x,T y) {return x<y?x=y,1:0;}
template<class T> inline bool Cmin(T &x,T y) {return x>y?x=y,1:0;}
typedef long long ll;
const int inf=1e9+1;
const int maxN=50+5;
const int maxn=maxN*maxN*maxN;
int N,X,D,p[maxN];
int num0[maxN],num1[maxN];
int dp[maxn];
struct node {
	ll x; int y,id;
	inline bool operator <(const node &other) const {
		return y*other.x>other.y*x;
	}
} a[maxN];
int sol(int X) {
	int an=0;
	for(int i=1;i<=N;++i) {
		int d=min(num0[i],int(X/a[i].x));
		an+=d*a[i].y,X-=d*a[i].x;
	}
	return an;
}
int main() {
	rd(N),rd(X),rd(D);
	rd(a[1].x),a[1].id=1;
	for(int i=2;i<=N;++i) rd(a[i].x),rd(p[i]),a[i].id=i;
	for(int i=N;i>=1;--i) {
		++a[i].y;
		a[p[i]].x+=a[i].x,a[p[i]].y+=a[i].y;
	}
	sort(a+1,a+N+1);
	for(int i=1;i<=N;++i) {
		num0[i]=a[i].id==1?inf:D;
		num1[i]=min(num0[i],N),num0[i]-=num1[i];
	}
	int n=N*N*N;
	for(int i=1;i<=n;++i) dp[i]=inf;
	for(int i=1;i<=N;++i) {
		for(int j=1,t=num1[i];t;t-=j,j=min(j<<1,t)) {
			int v=a[i].y*j; ll w=a[i].x*j;
			for(int k=n;k>=v;--k) dp[k]=min((ll)dp[k],dp[k-v]+w);
		}
	}
	int an=0;
	for(int i=0;i<=n;++i) if(dp[i]<=X) {
//		debug("%d %d\n",i,dp[i]);
		Cmax(an,i+sol(X-dp[i]));
	}
	printf("%d\n",an);
	return 0;
} 
posted @ 2020-07-15 18:35  LJZ_C  阅读(130)  评论(0编辑  收藏  举报