T712759 都因为你 (并非贪心) 赛后题解

题目传送门

思路

观察 \(\sqrt[\sum_{j\in S}{b_j}]{\prod_{j\in S} a_{j}}\) ,发现这是一个背包问题。

设计 dp[i][j] 表示考虑前 \(i\) 个物品,\(\sum_{k\in S}{b_k}=j\) 的最大的 \(\prod_{j\in S}a_{j}\)
转移方程大致为 dp[i][j]=max(dp[i-1][j],dp[i-1][j-b[i]]*a[i])
答案为 \(\max\{\sqrt[i]{dp[n][i]}\}\)

目测可拿 0 分。


发现有 \(\log_2\) 这个东西,它可以大幅减小 \(a_i\),并在 dp 时可将乘法转化为加法。

做法

设计 dp[i][j] 表示考虑前 \(i\) 个物品,\(\sum_{k\in S}{b_k}=j\) 的最大的 \(\sum_{j\in S}\log_2a_{j}\)
转移方程大致为 dp[i][j]=max(dp[i-1][j],dp[i-1][j-b[i]]+c[i])\(c_i\) 表示 \(\log_2a_i\)
答案为 \(2^{\max\{\frac{dp[n][i]}{i}\}}\)

再用滚动背包优化一下空间。

时间复杂度:\(O(n\times\sum{b})\),可以通过 \(n\le5000,a_i\le10^{18}\)

代码
#include<bits/stdc++.h>
using namespace std;
struct node
{
	double a;
	int b;
}a[5005];
long double dp[8005];
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	freopen("dp.in", "r", stdin);
	freopen("dp.out", "w", stdout);
	int n, l, r;
	cin >> n >> l >> r;
	for(int i = 1; i <= n; i ++)
	{
		cin >> a[i].a >> a[i].b;
		a[i].a = log2(a[i].a);
	}
	memset(dp, ~0x3f, sizeof(dp));//记得初始化
	dp[0] = 0;
	for(int i = 1; i <= n; i ++)
	{
		for(int j = 8000; j >= a[i].b; j --)
		{
			dp[j] = max(dp[j], dp[j - a[i].b] + a[i].a);
		}
	}
	long double ans = 0;
	for(int i = l; i <= r; i ++)
	{
		ans = max(ans, dp[i] / (long double)i);
	}
	cout << fixed << setprecision(8) << pow(2, ans);
	return 0;
}

posted @ 2025-12-20 15:02  yuzihang  阅读(0)  评论(0)    收藏  举报