当前位置: 代码迷 >> 综合 >> 【codeforces757 Div2 D1】Divan and Kostomuksha(1614D1)题解
  详细解决方案

【codeforces757 Div2 D1】Divan and Kostomuksha(1614D1)题解

热度:24   发布时间:2024-01-21 21:09:12.0

题目大意

给定 n n n个正整数 a 1 , 2... n a_{1,2...n} a1,2...n?
可以任意改变顺序。
g c d ( a 1 ) + g c d ( a 1 , a 2 ) + . . . + g c d ( a 1 , a 2 , . . . , a n ) gcd(a_1) + gcd(a_1,a_2)+...+gcd(a_1,a_2,...,a_n) gcd(a1?)+gcd(a1?,a2?)+...+gcd(a1?,a2?,...,an?)的最大值。

原题地址

思路

直接求 g c d gcd gcd时间复杂度肯定会炸。所以我们用 c n t [ j ] cnt[j] cnt[j]表示因数包含 j j j的数的个数,用这样的计算方式代替求 g c d gcd gcd

借助 c n t cnt cnt数组计算

这个前缀 g c d gcd gcd大小会逐渐下降的。
假设最开始 g c d gcd gcd x x x,那么对答案的贡献就是 x ? c n t [ x ] x * cnt[x] x?cnt[x];
然后假设 g c d gcd gcd下降到了 y y y,那么可以推出 y ∣ x y | x yx,即$ (x \mod y = 0) , 我 们 来 计 算 这 一 部 分 数 的 贡 献 , 因 为 实 际 上 ,我们来计算这一部分数的贡献, 因为实际上 ,,cnt[y] 包 含 了 包含了 cnt[x] , 而 ,而 ,cnt[x] 已 经 被 计 算 过 了 , 所 以 要 减 去 它 们 , 为 已经被计算过了, 所以要减去它们,为 ,,y * (cnt[y]-cnt[x])$。

DP

上述分析并不能解出本题,但帮我们了解了递推关系。
最优解法肯定与这种思路类似:把尽量大的,且与后面的数gcd大的数尽量放在前面
但是这么想是很难去推的,所以我们应该逆着来推:
假设最开始前面的数的 g c d gcd gcd就是 1 1 1,那么初始结果就是 n n n,然后我们不断地把gcd大的数放在前面,计算这么做 增大的部分。

  • 假设最开始所有数的 g c d gcd gcd均为 y y y(实际上最开始我们是从 1 1 1开始的),那么初始结果就是 c n t [ y ] ? y cnt[y] * y cnt[y]?y
  • 然后我们把所有 g c d gcd gcd x x x的数放在前面( y ∣ x y|x yx),那么增加的结果改怎么计算呢? c n t [ x ] cnt[x] cnt[x]中的每个数在计算 c n t [ y ] cnt[y] cnt[y]的贡献时,已经被计算了一次,所以我们应该计算它门多出来的部分,即 ( x ? y ) ? c n t [ x ] (x - y) * cnt[x] (x?y)?cnt[x]
  • 然后我们再把 c n t [ x ] cnt[x] cnt[x]中, g c d gcd gcd z ( x ∣ z ) z(x | z) z(xz)的数放在前面,再次计算增加的数。

一直重复上述过程。

当然 g c d gcd gcd可能会有很多个,我们并不能确定把哪个放在前面是最优的,所以此时我们就要用 D P DP DP的做法了。
那么我们改动一下上述推算结果,状态转移方程就能写出来了
d p [ j ] = m a x ( d p [ j ] , d p [ i ] + ( j ? i ) ? c n t [ j ] ) dp[j] = max(dp[j], dp[i] + (j - i) * cnt[j]) dp[j]=max(dp[j],dp[i]+(j?i)?cnt[j]),其中 i ∣ j i | j ij

代码

#include <cstdio>
#include <iostream>
using namespace std;const int maxN = 5e6;int n;
long long dp[maxN + 10], cnt[maxN + 10];int main()
{
    scanf("%d", &n);for(int i = 1; i <= n; ++i) {
    int x;scanf("%d", &x);cnt[x]++;}for(int i = 1; i <= maxN; ++i)for(int j = 2 * i; j <= maxN; j += i)cnt[i] += cnt[j];dp[1] = n;   long long ans = 0;     for(int i = 1; i <= maxN; ++i)for(int j = 2 * i; j <= maxN; j += i)dp[j] = max(dp[j], (j - i) * cnt[j] + dp[i]);for(int i = 1; i <= maxN; ++i)ans = max(ans, dp[i]);printf("%lld\n", ans);            return 0;
}
  相关解决方案