CF1614D1
首先,若 \(j\vert i\),\(j\) 一定放在 \(i\) 的前面。
然后,对于这道题显然考虑 dp:\(dp_i\) 表示选一些数排列,使它们的 \(gcd\) 是 \(i\) 的倍数(没说一定是 \(i\)),最大的 gcd 前缀和。
如果直接硬上,会发现不知道哪些数字用过,还要加个状压。
但我们发现,当从 \(dp_j\) 转移到 \(dp_i\) 时(\(j\vert i\)),如果该方案是最优的,那么所有 \(j\) 的倍数一定在这次转移之前就已经用了(就是一开始的那个性质),我们在后面加的,一定是 \(i\) 的倍数并且不是 \(j\) 的倍数。
所以设 \(cnt_i\) 表示满足 \(i\vert a_j\) 的 \(j\) 个数,转移方程为:\(dp_i=\max\{dp_j+(cnt_i-cnt_j)\cdot i\}\)。
时间复杂度 \(O(v\log v)\),\(v\) 为值域大小。
#include <cstdio>
#define int long long
inline int max(const int x, const int y) {return x > y ? x : y;}
int cnt[5000005], dp[5000005], ans;
signed main() {
int n;
scanf("%lld", &n);
for (int i = 1, x; i <= n; ++ i) scanf("%lld", &x), ++ cnt[x];
for (int i = 1; i <= 5000000; ++ i)
for (int j = i + i; j <= 5000000; j += i) cnt[i] += cnt[j];
for (int i = 5000000; i; -- i) {
dp[i] = cnt[i] * i;
for (int j = 2 * i; j <= 5000000; j += i) dp[i] = max(dp[i], dp[j] + i * (cnt[i] - cnt[j]));
ans = max(ans, dp[i]);
}
printf("%lld", ans);
}