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);
}
posted @ 2021-11-28 14:00  zqs2020  阅读(56)  评论(0编辑  收藏  举报