After playing with her beautiful array, Mishka decided to learn some math. After learning how to multiply, divide and what is divisibility, she is now interested in solving the following problem.
You are given integer k and array a1, a2, ..., an of n integers. You are to find non-empty subsequence of array elements such that the product of its elements is divisible by k and it contains minimum possible number of elements.
Formally, you are to find a sequence of indices 1 ≤ i1 < i2 < ... < im ≤ n such that is divisible by k while m is minimum possible among all such variants.
If there are more than one such subsequences, you should choose one among them, such that sum of its elements is minimum possible.
Mishka quickly solved this problem. Will you do so?
The first line of the input contains two integers n and k (1 ≤ n ≤ 1 000, 1 ≤ k ≤ 1012).
The second line of the input contains n integers a1, a2, ..., an (1 ≤ ai ≤ 1012) — array elements.
Print single positive integer m in the first line — the number of elements in desired sequence.
In the second line print m distinct integers — the sequence of indices of given array elements, which should be taken into the desired sequence.
If there are more than one such subsequence (e.g. subsequence of minimum possible number of elements and with minimum possible sum of elements), you can print any of them.
If there are no such subsequences, print - 1 in the only line.
5 60 2 4 6 5 2
3 4 3 1
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 1010, M = 0, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
int n, m; LL K;
LL a[N];
LL b[N];
vector<LL>v;
#include<unordered_map>
unordered_map<LL, int>mop;
pair<int, LL>f[N][N * 10];
#define num first
#define sum second
LL gcd(LL x, LL y)
{
return y == 0 ? x : gcd(y, x % y);
}
//处理K的所有约数,并使得每个约数对应映射到特定编号
void init()
{
v.clear();
int top = sqrt(K + 0.5);
for (int i = 1; i <= top; ++i)if (K % i == 0)
{
v.push_back(i);
if (K / i != i)v.push_back(K / i);
}
sort(v.begin(), v.end());
m = v.size() - 1;
mop.clear(); for (int i = 0; i <= m; ++i)mop[v[i]] = i;
for (int i = 1; i <= n; ++i)
{
scanf("%lld", &a[i]);
b[i] = gcd(a[i], K);
}
}
void dp()
{
if (K == 1)
{
puts("1");
printf("%lld\n", min_element(a + 1, a + n + 1) - a);
return;
}
for (int j = 1; j <= m; ++j)f[0][j] = { n + 1, 0 };
for (int i = 1; i <= n; ++i)
{
for (int j = 0; j <= m; ++j)
{
f[i][j] = f[i - 1][j];
int pre = mop[v[j] / gcd(v[j], b[i])];
gmin(f[i][j], MP(f[i - 1][pre].num + 1, f[i - 1][pre].sum + a[i]));
}
}
if (f[n][m].num > n)puts("-1");
else
{
printf("%d\n", f[n][m].num);
for (int i = n; i; --i)
{
if (f[i][mop[K]] != f[i - 1][mop[K]])
{
printf("%d ", i);
K /= gcd(K, b[i]);
}
}
puts("");
}
}
int main()
{
while (~scanf("%d%lld", &n, &K))
{
init();
dp();
}
return 0;
}
/*
【trick&&吐槽】
1,对gcd的预处理是个很好的优化
2,在输出答案的时候,不能连续使得K/=b[i],而应当使得K/=gcd(K,b[i])才能找到其合法前驱
3,如果输入的K为1,这已经是个终止状态了,但是我们依然需要至少一个数
【题意】
有n个数,我们希望选出最少数量的数,使得这些数的乘积为K的倍数
【类型】
乘除法DP map映射
【分析】
这道题是一个DP
数字个数不多,我们可以考虑逐渐枚举每个数x,逐一加进来。
然后我们当前的若干个数的乘积,有哪些可行的状态呢?
显然,之前所有数乘积的意义,是达到了K的一个约数的位置。
于是我们可以枚举当前所达到的约数j。
然后这个数对答案的贡献最大为gcd(j, x);
那么其前驱状态节点就为j/gcd(j,x)
我们用f[i][j]表示当前考虑了前i个数,当gcd(所选数的乘积,K)==j时的最优方案
那么有f[i][j]由f[i-1][j/gcd(j,x)]转移而来。
j/gcd(j,x),使得我们考虑了当前加入数的最大贡献,使得其前驱节点尽可能小。
为什么这样子呢?难道不能是前驱节点大一些反而答案更优么?
因为啊,我们的DP保证了解的单调性,即f[i][j的约数]的状态,一定是比f[i][j]要优的。
这个可以用数学归纳法证明。
其实很显然,初始f[0][0]最优;在之后,凡是f[i][j]可以获得的转移途径,f[i][j的约数]都可以通过更小的前驱转移而来。
转移当步的成本增加是相同,转移前驱的状态会更优,当前状态也就会更优。
于是,这样直到最后,就可以生成最优答案。
不过,我们不能直接用j==约数表示状态,这个是爆炸的。
所以我们要用j==约数编号表示状态,同时用map做约数->约数编号的哈希。
最后只要模拟求解的过程,在DP中找到路径方案输出即可。
【时间复杂度&&优化】
O(n * K的约数个数)
*/