E. Mishka and Divisors Codeforces Round #365 (Div. 2) 01背包

E. Mishka and Divisors
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

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 ofn integers. You are to find non-empty subsequence of array elements such that the product of its elements is divisible byk 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 byk 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 isminimum possible.

Mishka quickly solved this problem. Will you do so?

Input

The first line of the input contains two integers n andk (1 ≤ n ≤ 1 000,1 ≤ k ≤ 1012).

The second line of the input contains n integersa1, a2, ..., an (1 ≤ ai ≤ 1012) — array elements.

Output

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.

Example
Input
5 60
2 4 6 5 2
Output
3
4 3 1 

做这种难题(对于我)真的爽,就算开始一点头绪也没有,但是如果弄懂了会学到很多东西。
这道题01背包,看题目描述的话应该想到类似的解法,就是每个数字只能选一次,让你求出选最优状态,这就类似01背包。
那么怎么规范状态呢。
我们分析一下就可以知道,比如说60的因数是1 2 3 4 5 6 10 12 15 20 30 60这12个数。
设dp【i】【j】的意义为前i个数中选最少的数字相乘为j的倍数的数量。
1.dp[i][j]非递减
2.我们想要得到60,我们可以从剩余11个数字状态转移而来、
3.我们结合01背包,每个数字只有取或者不取,如果不取,那么dp【i】【j】=dp【i-1】【j】,去了就是dp【i】【j】=dp【i-1】【j/gcd(j,s【i】)】
+1(s【i】为给定的数字)
为什么要从dp【i-1】【j/gcd(j,s【i】)】转移而来呢?刚刚说过,dp数组非递减,那么我们要使dp【i】【j】中的j尽可能的小,这样得出的结果才能尽可能的小。
如果j/gcd(j,s【i】)都要比不放要大的话,那么其他的因数自然也就不成立,如果无法达成j/gcd(j,s【i】)这个状态的话,那么其他的因数状态也无法完成,所以这就是正确
的转移方式。
注意,这道题卡常数,非常恶心。在gcd的时候,不要直接gcd(j,s【i】),要先st【i】=gcd(k,s【i】),然后j/gcd(j,st【i】),这样可以少跑一点。

#include<bits/stdc++.h>
using namespace std;
long long s[1008],st[1008];
int pre[1008][10080];
int dp[10008];
long long sum[10008];
vector<long long> factor;
map<long long,int>maped;
int div(long long k)
{
    for(long long i=1; i*i<=k; i++)
    {
        if(k%i==0)
        {
            factor.push_back(i);
            factor.push_back(k/i);
        }
    }
    sort(factor.begin(),factor.end());
    return unique(factor.begin(),factor.end())-factor.begin();
}
int main()
{
    int n;
    long long k;
    long long Min=LLONG_MAX;
    int pos;
    while(scanf("%d%I64d",&n,&k)!=EOF)
    {
        for(int i=1; i<=n; i++)
        {
            scanf("%I64d",&s[i]);
            st[i]=__gcd(k,s[i]);
            if(Min>s[i])
            {
                Min=s[i];
                pos=i;
            }
        }
        if(k==1)
        {
            printf("%d\n",1);
            printf("%d\n",pos);
            continue;
        }
        memset(pre,-1,sizeof(pre));
        memset(sum,0x3f,sizeof(sum));
        memset(dp,0x3f,sizeof(dp));
        dp[0]=sum[0]=0;
        factor.clear();
        maped.clear();
        int len=div(k);
        for(int i=0; i<len; i++)
        {
            maped[factor[i]]=i;
        }
        for(int i=1; i<=n; i++)
        {
            for(int j=len-1; j>=1; j--)
            {
                long long t=__gcd(factor[j],(long long)st[i]);
                int pos=maped[factor[j]/t];
                if(dp[j]>dp[pos]+1)
                {
                    dp[j]=dp[pos]+1;
                    sum[j]=sum[pos]+s[i];
                    pre[i][j]=pos;
                }
                if(dp[j]==dp[pos]+1&&sum[j]>sum[pos]+s[i])
                {
                    sum[j]=sum[pos]+s[i];
                    pre[i][j]=pos;
                }
            }
        }
        if(dp[len-1]>n)
        {
            printf("-1\n");
        }
        else
        {
            printf("%d\n",dp[len-1]);
            int a=n,b=len-1;
            while(a>0&&b>0)
            {
                while(pre[a][b]==-1&&a>0&&b>0)
                    a--;
                printf("%d ",a);
                b=pre[a--][b];
            }

        }
        printf("\n");

    }
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值