Mishka and Divisors[CodeForces Round #365 Div.2]

http://codeforces.com/contest/703/problem/E

题意:给定一个最多个数的序列,从中选出最少个数的数字,使得他们的乘积是k的倍数,若有多种选择方式,输出选出数字和最小的一种,若有多种,输出任意一种。

动态规划,dp[i][j]表示从前i个数里选,所得乘积是j的倍数。显然dp[i][j]=max(dp[i-1][j],dp[i-1][j/gcd(j,a[i])])。由于k可能很大,所以只需令j分别等于k的每个约数即可。

确定k的约数的时候令i从1到sqrt(k)循环判断,注意由于k很大,这里的i要使用longlong。

#include<bits/stdc++.h>
using namespace std;
#define ft first
#define sd second
#define mp make_pair
long long a[1100], k, f[11000], b[1100];
int fc, n;
pair<int, long long> dp[1100][11000];
map<long long , int> e;
int main()
{
    //freopen("input.txt", "r", stdin);
    scanf("%d%I64d", &n, &k);
    long long t = k;
    for (int i = 1; i <= n; i++)
    {
        scanf("%I64d", &a[i]);
        b[i] = __gcd(k, a[i]);
        t /= __gcd(t, a[i]);
    }
    if (t != 1)
    {
        printf("-1
");
        return 0;
    }
    if (k == 1)
    {
        printf("1
%d
", (int)(min_element(a + 1, a + n + 1) - a));
        return 0;
    }
    e.clear();
    fc = 0;
    for (long long i = 1; i * i <= k; i++)
    {
        if (k % i != 0) continue;
        f[fc++] = i;
        if (i * i != k) f[fc++] = k / i;
    }
    sort(f, f + fc);
    for (int i = 0; i < fc; i++)
        e[f[i]] = i;
    for (int i = 1; i < fc; i++)
        dp[0][i] = mp(n + 1, 0);
    dp[0][0] = mp(0, 0);
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < fc; j++)
        {
            dp[i][j] = dp[i - 1][j];
            long long v = e[f[j] / __gcd(f[j], b[i])];
            if (dp[i][j] > mp(dp[i - 1][v].ft + 1, dp[i - 1][v].sd + a[i]))
                dp[i][j] = mp(dp[i - 1][v].ft + 1, dp[i - 1][v].sd + a[i]);
        }
    printf("%d
", dp[n][fc - 1].ft);
    t = k;
    for (int i = n; i > 0; i--)
    {
        if (dp[i][e[t]] == dp[i - 1][e[t]]) continue;
        printf("%d ", i);
        t /= __gcd(t, b[i]);
    }
    printf("
");
    //fclose(stdin);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/dramstadt/p/5759206.html