[ARC096D] Sweet Alchemy

考虑把题目给的限制关系建为一棵树,则问题变为每次选择一棵子树,消耗 \(\Sigma\)\(m_i\) ,造出 \(sz_i\) 个物品,每个物品至多选择限制 (\(D_i\)) 次。
然后考虑这就是一个多重背包问题,重量为 \(wi\)\(=\)\(\Sigma\)\(mi\) ,价值 \(vi=sz_i\)
算法瓶颈在于 \(D_i\) 太大,考虑缩小 \(D_i\)
我们有一个假的贪心,叫做优先使用 \(\frac{w_i}{v_i}\) 大的物品,那么在收益相等的情况下,用这种做法显然更优秀所以在此我们有一个重要结论就是当我们能选择 \(\frac{w_i}{v_i}\)\(>\)\(\frac{w_j}{v_j}\) 的物品 \(i\) 时,至多选择了 \(w_i - 1\) 个物品 \(j\)
然后二进制分组一下, 每个物品最多选择 \(min\){\(n,D\)} 次。
代码

#include<bits/stdc++.h>
#define RG register
#define LL long long
#define U(x, y, z) for(RG int x = y; x <= z; ++x)
#define D(x, y, z) for(RG int x = y; x >= z; --x)
using namespace std;
template <typename T> void read(T &n){ bool f = 1; char ch = getchar(); n = 0; for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = 0; for (; isdigit(ch); ch = getchar()) n = (n << 1) + (n << 3) + (ch ^ 48); if (f == 0) n = -n;}
inline char Getchar(){ char ch; for (ch = getchar(); !isalpha(ch); ch = getchar()); return ch;}
template <typename T> inline void write(T n){ char ch[60]; bool f = 1; int cnt = 0; if (n < 0) f = 0, n = -n; do{ch[++cnt] = char(n % 10 + 48); n /= 10; }while(n); if (f == 0) putchar('-'); for (; cnt; cnt--) putchar(ch[cnt]);}
template <typename T> inline void writeln(T n){write(n); putchar('\n');}
template <typename T> inline void writesp(T n){write(n); putchar(' ');}
template <typename T> inline void chkmin(T &x, T y){x = x < y ? x : y;}
template <typename T> inline void chkmax(T &x, T y){x = x > y ? x : y;}
template <typename T> inline T Min(T x, T y){return x < y ? x : y;}
template <typename T> inline T Max(T x, T y){return x > y ? x : y;}
inline void readstr(string &s) { s = ""; static char c = getchar(); while (isspace(c)) c = getchar(); while (!isspace(c)) s = s + c, c = getchar();}
inline void FO(string s){freopen((s + ".in").c_str(), "r", stdin); freopen((s + ".out").c_str(), "w", stdout);}

const int N = 60;

int n, X, cnt, d;
int head[N];
// int val[N], sz[N], m?[N], head[N];
LL dp[N * N * N];

struct edge{
	int v, nxt;
}e[N << 1];
struct Node{
	int w, id; LL v;
}a[N * N];
bool operator <(const Node &a, const Node &b) {
	return a.w * b.v > b.w * a.v;
}

inline void aedge(int u, int v) {
	e[++cnt].nxt = head[u];
	head[u] = cnt;
	e[cnt].v = v; 
}

void dfs(int u) {
	// cout << u << endl;
    a[u].w=1,a[u].id=u;
    for (int i=head[u];i;i=e[i].nxt)
        dfs(e[i].v),a[u].v+=a[e[i].v].v,a[u].w+=a[e[i].v].w;
}

int main(){
	//FO("");
	read(n), read(X), read(d);
	// a[1].v=read();
	read(a[1].v);
    for (int i=2;i<=n;++i){
       	read(a[i].v);
        int v;
        read(v);
        aedge(v, i);
    }
	dfs(1);

	int mx = n * n * n, L = min(n, d);
	memset(dp, 0x3f, sizeof dp); dp[0] = 0;
	U(i, 1, n) {
		int x = L;
		for (int j = 0; 1 << j <= x; ++j) {
			int w = a[i].w * (1 << j); LL v = a[i].v* (1 << j);
			D(k, mx, w) dp[k] = min(dp[k], dp[k - w] + v);
			x -= 1 << j;
		}
		if (x) {
			int w = a[i].w * x; LL v = a[i].v * x;
			D(k, mx, w) dp[k] = min(dp[k], dp[k - w] + v);
		}
	}

	sort(a + 1, a + n + 1);
	while (a[n].id != 1) --n;
	LL ans = 0;
	U(i, 0, mx) {
		if (dp[i] > X) continue ;
		LL w = i, v = dp[i];
		U(j, 1, n - 1) {
			LL c = min((LL) d - L, (X - v) / a[j].v);
			w += c * a[j].w, v += c * a[j].v;
		}
		LL c = (X - v) / a[n].v;
		w += c * a[n].w;
		chkmax(ans, w);
	}
	writeln(ans);
	return 0;
}
posted @ 2022-10-19 10:05  Southern_Way  阅读(29)  评论(0)    收藏  举报