Codeforces 703E Mishka and Divisors 离散化+DP

题意:给出长度为n的序列a,子序列合法:子序列中每个元素相乘后得到的乘积能被k整除
n<=1e3,k,a[i]<=1e12.找到长度最小的合法子序列(若长度最小的有多个,输出子序列和最小的)
 
设f[i][d]为 前i个数中选出乘积为d的倍数需要的最少个数.
f[i][d]=min(f[i-1][d],f[i-1][d/gcd(d,a[i])]+1)  (f[i][x]<=f[i][px])

离散化第二维,因为d每次都转移到自己的某个因子中.最坏情况下只有6720个因子..

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> ii;
const int N=1e3+5;
vector<ll> v;
map<ll,int>id;
ii f[N][8000];
ll n,k,m;
void init()
{
	v.clear();
	for(ll i=1;i*i<=k;i++)
	{
		if(k%i==0)
		{
			v.push_back(i);
			if(i*i!=k)
				v.push_back(k/i);
		}
	}
	sort(v.begin(),v.end());
	m=v.size()-1;
	id.clear();
	for(int i=0;i<=m;i++)
		id[v[i]]=i;	
}
ll a[N],b[N];
ll gcd(ll a,ll b)
{
	return b==0?a:gcd(b,a%b);
}
int main()
{
	while(cin>>n>>k)
	{
		init();	
		for(int i=1;i<=n;i++)
			scanf("%I64d",&a[i]),b[i]=gcd(a[i],k);
		if(k==1)
		{
			puts("1");
			printf("%d\n",min_element(a+1,a+1+n)-a);
			continue;
		}
		for(int j=1;j<=m;j++)
			f[0][j]=ii(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=id[v[j]/gcd(v[j],b[i])];
				ii t=ii(f[i-1][pre].first+1,f[i-1][pre].second+a[i]);
				f[i][j]=min(f[i][j],t);
			}
		}
		if(f[n][m].first>n)
			puts("-1");
		else
		{
			printf("%d\n",f[n][m].first);
			for(int i=n;i>=1;i--)
			{
				if(f[i][id[k]]!=f[i-1][id[k]])
				{
					printf("%d ",i);
					k/=gcd(k,b[i]);
				}
			}
		}
		printf("\n");
	}
	return 0;
}

ps:

a[i]较大,令b[i]=gcd(a[i],k) 求gcd(a[i],v[j])时,将a[i]用b[i]代替不影响结果 (第一步只是排除掉a[i]中不含k素因子的因子).

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值