codeforces 703E

1 篇文章 0 订阅
1 篇文章 0 订阅

题意:给出1000个数,和一个整数10^12。求个数最少,和最小的子集,使得他们的乘积是时k的倍数。

题解:注意到k的约束的个数不是很大,然后dp[i][d]=dp[i][d/gcd(a[i],d])]+1;注意到只有k的约束的才有意义,然后就是离散化,然后dp,细节比较多,请查看代码。


#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> pLL;
/*
 1.离散化
 2.dp
 */
#define N 1001000
vector<pLL> primeDiviser;
vector<vector<int> > aiConPi;
vector<int> mul;
int init(vector<LL> a,int n,LL k){
	//vector<bool> isPrim(N,true);
	for(LL i=2;i*i<=k;i++){
		//if(!isPrim[i]) continue;
//		for(LL j=i*i;j<N;j++){
//			isPrim[j] = false;
//		}
		if(k%i==0){
			pLL tmpPrimeDiviser = make_pair(i,0);
			while(k%i==0){
				tmpPrimeDiviser.second++;
				k/=i;
			}
			primeDiviser.push_back(tmpPrimeDiviser);
		}
	}
	if(k!=1){
		primeDiviser.push_back(make_pair(k,1));
	}

	//每个数包含的k的约数的个数
	aiConPi = vector<vector<int> > (n,vector<int>(primeDiviser.size(),0));
	for(int i=0;i<n;i++){
		for(int j=0;j<(int)primeDiviser.size();j++){
			while(a[i]%primeDiviser[j].first==0){
				aiConPi[i][j]++;
				a[i]/=primeDiviser[j].first;
			}
		}
	}

	//离散化用的
	mul = vector<int>(primeDiviser.size(),0);
	mul[primeDiviser.size()-1] = 1;
	for(int i=primeDiviser.size()-2;i>=0;i--){
		mul[i] = mul[i+1]*(primeDiviser[i+1].second+1);
	}

	int ret = 0;
	for(int i=primeDiviser.size()-1;i>=0;i--){
		ret += primeDiviser[i].second*mul[i];
	}
	return ret;  //最终的状态  目标状态
	//target status的状态是
}
const LL INF = (LL)1e18;
int main(){
	int n;
	LL k;

	while(cin>>n>>k){
		vector<LL> a(n);
		for(int i=0;i<n;i++){
			cin>>a[i];
		}

		if(k==1){//没有素因子
			LL ret = 0;
			for(int i=1;i<n;i++){
				if(a[ret]>a[i]){
					ret = i;
				}
			}
			cout<<1<<endl<<ret+1<<endl;
			continue;
		}
		int target = init(a,n,k);//找出全部素因子

		//cout<<"target:"<<target<<endl;

		vector<vector<pLL> > dp(n+1,vector<pLL>(target+1,make_pair(INF,0)));
		vector<vector<int> > prev(n+1,vector<int>(target+1,-1));
		dp[0][0]=make_pair(0,0);//
//		for(int i=0;i<n;i++){
//			for(int j=0;j<(int)primeDiviser.size();j++){
//				cout<<aiConPi[i][j]<<" ";
//			}
//			cout<<endl;
//		}
//		for(int i=0;i<(int)primeDiviser.size();i++){
//			cout<<mul[i]<<" ";
//		}
//		cout<<endl;
		for(int i=1;i<=n;i++){
			for(int j=0;j<=target;j++){
				dp[i][j] = dp[i-1][j];
				prev[i][j] = j;

				int last=0,cur=j;
				for(int e=0;e<(int)primeDiviser.size();e++){
					last += mul[e] * max(0,cur/mul[e]-aiConPi[i-1][e]);
					cur%=mul[e];
				}
//				cout<<"dp["<<i-1<<"]["<<last<<"]->"<<"dp["<<i<<"]["<<j<<"]"<<endl;
				if(dp[i][j] > make_pair(dp[i-1][last].first+1,dp[i-1][last].second+a[i-1])){
					dp[i][j] = make_pair(dp[i-1][last].first+1,dp[i-1][last].second+a[i-1]);
					prev[i][j] = last;
					//cout<<j<<","<<last<<endl;
				}
//				cout<<"dp["<<i<<"]["<<j<<"]:"<<dp[i][j].first<<","<<dp[i][j].second<<endl;
			}
		}
		//cout<<"get here"<<endl;
		if(dp[n][target] == (pLL) make_pair(INF,0)){
			cout<<"-1"<<endl;
		}else{
			cout<<dp[n][target].first<<endl;
			for(int i=n,j=target;i>=1&&j!=0;j = prev[i][j],i--){
				if(prev[i][j]==j){
					continue;
				}else{
					cout<<i<<" ";
				}
//				cout<<j<<endl;
			}
			cout<<endl;
		}
	}
}
/*

2 1
1000000000000
999999999999
 */


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值