CF1614D1 Divan and Kostomuksha (easy version)

题目大意

给定序列 a1,a2,...,ana_1,a_2,...,a_n,要求重排 aa,使得

i=1ngcd(a1,a2,...,ai)\sum\limits_{i=1}^n \gcd(a_1,a_2,...,a_i)

最大。

输出这个最大值。

解题思路

显然,一道 dp

cnticnt_i 表示数组 aa 中是 ii 的倍数的元素个数,dpidp_i 表示时能得到已含有因数 ii 结尾排列能获得的最大值。

此时有转移方程

dpi=maxj=1fi×prij+i×(cnticnti×prij)dp_i=\max_{j=1}{f_{i \times pri_j} + i \times (cnt_i - cnt_{i \times pri_j})}

最后答案为满足 cnti=ncnt_i=ndpidp_i 的最大值。

cntcnt 可先筛出所有质数,再枚举 ii 和质数集 pripri,计算 cnticnt_i

时间复杂度 O(wloglogw)\mathcal O(w \log \log w),其中 ww 为值域。

具体实现见代码。

CODE

#include <bits/stdc++.h>

using namespace std;

#define int long long

inline int read()
{
    int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9')
    {
        if (c == '-')
            f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
    {
        x = x * 10 + c - '0';
        c = getchar();
    }
    return x * f;
}

const int _ = 2e7 + 1;

int n;

int ans;

int cnt[_ + 7];

int dp[_ + 7];

bool vis[_];

vector<int> primes;

void init()
{
    for (int i = 2; i < _; ++i)
    {
        if (!vis[i])
            primes.push_back(i);
        for (auto p : primes)
        {
            if (p * i > _)
                break;
            vis[p * i] = 1;
            if (i % p == 0)
                break;
        }
    }
}

signed main()
{
    init();
    n = read();
    for (int i = 1, x; i <= n; ++i)
        cnt[read()]++;
    for (auto p : primes)
        for (int j = _ / p; j >= 1; --j)
            cnt[j] += cnt[j * p];
    for (int i = _ - 1; i >= 1; --i)
    {
        dp[i] = cnt[i] * i;
        for (auto p : primes)
        {
            int j = p * i;
            if (j > _)
                break;
            dp[i] = max(dp[i], dp[j] + i * (cnt[i] - cnt[j]));
        }
        if (cnt[i] == n)
            ans = max(ans, dp[i]);
    }
    printf("%lld\n", ans);
    return 0;
}
posted @ 2021-11-28 13:43  蒟蒻orz  阅读(7)  评论(0编辑  收藏  举报  来源