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; }