LOJ6039 「雅礼集训 2017 Day5」珠宝

发布时间 2023-12-07 18:55:27作者: zltzlt

LOJ 传送门

显然枚举物品做背包没有前途,于是我们把体积相等的物品捆绑在一起。

\(f_{i, j}\) 为考虑完体积 \(\in [1, i]\) 的物品,背包容量为 \(j\) 的最大值。可以贪心求出 \(g_{i, j}\) 为选 \(j\) 个体积为 \(i\) 的物品的价值最大值。

\(j \bmod i\) 的余数转移,发现可以决策单调性,然后就 \(O(k c_i \log k)\) 做完了。?

code
// Problem: #6039. 「雅礼集训 2017 Day5」珠宝 /「NAIPC2016」Jewel Thief
// Contest: LibreOJ
// URL: https://loj.ac/p/6039
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 50050;

ll n, m, f[310][maxn], g[maxn], h[maxn], K;
vector<ll> a[maxn], b[maxn];

void dfs(int l, int r, int pl, int pr, int k) {
	if (l > r || pl > pr) {
		return;
	}
	int mid = (l + r) >> 1, p = -1;
	for (int i = max(pl, mid - (int)a[k].size()); i <= min(mid, pr); ++i) {
		ll val = g[i] + b[k][mid - i];
		if (val > h[mid]) {
			h[mid] = val;
			p = i;
		}
	}
	dfs(l, mid, pl, p, k);
	dfs(mid + 1, r, p, pr, k);
}

void solve() {
	scanf("%lld%lld", &n, &m);
	for (int _ = 0, x, y; _ < n; ++_) {
		scanf("%d%d", &x, &y);
		a[x].pb(y);
	}
	for (int i = 1; i <= 300; ++i) {
		sort(a[i].begin(), a[i].end(), greater<ll>());
		ll s = 0;
		b[i].pb(0);
		for (ll x : a[i]) {
			s += x;
			b[i].pb(s);
		}
	}
	for (int i = 1; i <= 300; ++i) {
		for (int j = 0; j < i; ++j) {
			K = 0;
			for (int k = j; k <= m; k += i) {
				g[++K] = f[i - 1][k];
				h[K] = -1e18;
			}
			dfs(1, K, 1, K, i);
			for (int k = j, t = 1; k <= m; k += i, ++t) {
				f[i][k] = h[t];
			}
		}
	}
	for (int i = 1; i <= m; ++i) {
		printf("%lld ", f[300][i]);
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}