[2020 CCPC 威海 L] Clock Master
https://codeforces.ml/gym/102798/problem/L
题意:
转换一下题意,就是问你现有\(b\)元,物品\(i(1 \leq i \leq 30000)\)拥有的权值和花费都是它对应的下标,问你怎么买使得你购买的物品的权值的\(LCM\)最大,输出\(ln(LCM)\)。
思路:
用钱买东西,往背包去靠。我们想要让物品权值\(LCM\)最大,应该让所有物品的权值互质。然后我们再考虑一下,如果一个数字有多个质因数,那么它一下子会占多个坑,同时它本身的大小也比它质因数的和要大,很容易会让我们最终求出来的\(LCM\)变小。例如,我们取了\(6\),那么所有\(2\)的次方数和\(3\)的次方数都不能取了,但是我们只取\(2\)和\(3\),只用花费\(5\)元就可以对LCM产生\(6\)个贡献。因此,我们考虑只取素数的次方数,并且每个素数的次方数中,我们只能取一个。这道题就变成了一道多组背包问题。同时注意到每次取的时候,物品都是不变的,而\(1 \leq T, b \leq 30000\),我们需要提前预处理出所有情况,最后\(O(1)\)查询就可以了,\(dp\)里直接传\(ln\)值的和。
训练的时候这道题心态都写崩了。不知道\(ln\)能直接求,先写了个二分求的,然后之后一直瞎构造,最后都没搞出来。对于用钱买东西还不够敏感,没有往背包上去想,太菜了QAQ
#include <bits/stdc++.h>
using namespace std;
const int N = 3e4;
int flag[N + 7], prime[N + 7], cnt;
double dp[N + 7], ln[N + 7];
vector<int> v[N];
int n;
void isprime() {
for (int i = 2; i <= N; ++i) {
if (!flag[i]) {
prime[++cnt] = i;
}
for (int j = 1; j <= cnt; ++j) {
if (1ll * i * prime[j] > N) break;
flag[i * prime[j]] = 1;
if (i % prime[j] == 0) break;
}
}
}
void init() {
isprime();
for (int i = 1; i <= cnt; ++i) {
int now = prime[i];
v[i].push_back(now);
while (1) {
now *= prime[i];
if (now > 30000) break;
v[i].push_back(now);
}
}
for (int i = 1; i <= 30000; ++i) {
ln[i] = log(i);
}
memset(dp, 0xfe, sizeof(dp));
dp[0] = 0;
for (int i = 1; i <= cnt; ++i) {
for (int w = 30000; w >= 1; --w) {
for (int j = 0; j < v[i].size(); ++j) {
int val = v[i][j];
if (w < val) break;
dp[w] = max(dp[w - val] + ln[val], dp[w]);
}
}
}
dp[1] = 0;
double maxx = dp[1];
for (int i = 2; i <= 30000; ++i) {
dp[i] = max(dp[i], maxx);
maxx = dp[i];
}
}
void solve() {
scanf("%d", &n);
printf("%.9f\n", dp[n]);
}
int main() {
init();
int t = 1;
scanf("%d", &t);
while (t--) solve();
return 0;
}