cf/codeforces #365 E - Mishka and Divisors 数学+背包dp+gcd
2016-08-31 13:58
344 查看
Problem E - Mishka and Divisors
题目大意
给n,k,给n个数,让你挑出m个数,使得m个数的乘积是k的倍数,输出最小的m。
如果有多个方案,输出sum最小的方案,(方案输出所选数的下标)
数据范围:n <= 1000, k <= 10^12, ai<=10^12。
解题分析
dp[i][j]表示前i个数里选一些数,使得乘积是j的倍数的最小的m。
sum[i][j]表示最优方案对应的sum
可以得知,dp[i][j]=min(dp[i-1][j],dp[i-1] [ j/gcd(j,a[i])] )
也就是要么不选a[i],如果选a[i]就在前面选前i-1个数取到j/gcd(j,a[i])的方案
记得相应的sum值也要更新。
这里的i就是1到n,而j应该是 k的约数们,可以知道k的约数个数大概是lg至上,sqrt未满的一个数量级,
粗略估计一下不超过1W ? 不过最后内存用了17X mb ...还好是cf
把k的约数们用map映射一下就好了
时间卡得紧,会T,注意到 求gcd(j,a[i])的时候,可以先令b[i]=gcd(k,a[i]),最后直接求gcd(j,b[i])能省一些时间
参考程序
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 1005;
ll aa
;
ll bb
;
ll prim[11000+50];
ll ok=0;
void f(ll k)
{
for (ll i=1; i*i<=k; i++)
if (k%i==0)
{
prim[++ok]=i;
if (k/i!=i)
prim[++ok]=k/i;
}
}
ll dp
[11000+50];
ll sum
[11000+5];
map <ll,int>idx;
ll gcd(ll a,ll b)
{
if (!b) return a;
else return gcd(b,a%b);
}
int main()
{
ll n,k;
cin>>n>>k;
ll tmp=k;
for (int i=1; i<=n; i++)
{
scanf("%lld",&aa[i]);
bb[i]=gcd(k,aa[i]);
}
if (k==1)
{
printf("1\n");
printf("%d\n",(min_element(aa+1,aa+1+n)-aa));
return 0;
}
f(k);
sort(prim+1,prim+1+ok);
for (int i=1; i<=ok; i++)
idx[prim[i]]=i;
for (int j=2; j<=ok; j++)
{
if (aa[1]%prim[j]==0)
dp[1][j]=1,sum[1][j]=aa[1];
else dp[1][j]=n+1;
}
dp[1][1]=0;
for (int i=2; i<=n; i++)
{
for (int j=1; j<=ok; j++)
{
dp[i][j]=dp[i-1][j];
sum[i][j]=sum[i-1][j];
ll v=prim[j]/gcd(prim[j],bb[i]);
int id=idx[v];
if (dp[i-1][id]+1 < dp[i][j] )
{
dp[i][j]=dp[i-1][id]+1;
sum[i][j]=sum[i-1][id]+aa[i];
}
else if (dp[i-1][id]+1==dp[i][j])
{
if (sum[i][j]>sum[i-1][id]+aa[i])
sum[i][j]=sum[i-1][id]+aa[i];
}
}
}
if (dp
[ok]>n)
{
printf("-1\n");
return 0;
}
printf("%lld\n",dp
[ok]);
tmp=k;
for (int i=n; i>=1; i--)
{
int id=idx[tmp];
if (dp[i][id]==dp[i-1][id]&& sum[i][id]==sum[i-1][id]) continue;
printf("%d ",i);
tmp/=gcd(tmp,bb[i]);
}
printf("\n");
return 0;
}
题目大意
给n,k,给n个数,让你挑出m个数,使得m个数的乘积是k的倍数,输出最小的m。
如果有多个方案,输出sum最小的方案,(方案输出所选数的下标)
数据范围:n <= 1000, k <= 10^12, ai<=10^12。
解题分析
dp[i][j]表示前i个数里选一些数,使得乘积是j的倍数的最小的m。
sum[i][j]表示最优方案对应的sum
可以得知,dp[i][j]=min(dp[i-1][j],dp[i-1] [ j/gcd(j,a[i])] )
也就是要么不选a[i],如果选a[i]就在前面选前i-1个数取到j/gcd(j,a[i])的方案
记得相应的sum值也要更新。
这里的i就是1到n,而j应该是 k的约数们,可以知道k的约数个数大概是lg至上,sqrt未满的一个数量级,
粗略估计一下不超过1W ? 不过最后内存用了17X mb ...还好是cf
把k的约数们用map映射一下就好了
时间卡得紧,会T,注意到 求gcd(j,a[i])的时候,可以先令b[i]=gcd(k,a[i]),最后直接求gcd(j,b[i])能省一些时间
参考程序
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 1005;
ll aa
;
ll bb
;
ll prim[11000+50];
ll ok=0;
void f(ll k)
{
for (ll i=1; i*i<=k; i++)
if (k%i==0)
{
prim[++ok]=i;
if (k/i!=i)
prim[++ok]=k/i;
}
}
ll dp
[11000+50];
ll sum
[11000+5];
map <ll,int>idx;
ll gcd(ll a,ll b)
{
if (!b) return a;
else return gcd(b,a%b);
}
int main()
{
ll n,k;
cin>>n>>k;
ll tmp=k;
for (int i=1; i<=n; i++)
{
scanf("%lld",&aa[i]);
bb[i]=gcd(k,aa[i]);
}
if (k==1)
{
printf("1\n");
printf("%d\n",(min_element(aa+1,aa+1+n)-aa));
return 0;
}
f(k);
sort(prim+1,prim+1+ok);
for (int i=1; i<=ok; i++)
idx[prim[i]]=i;
for (int j=2; j<=ok; j++)
{
if (aa[1]%prim[j]==0)
dp[1][j]=1,sum[1][j]=aa[1];
else dp[1][j]=n+1;
}
dp[1][1]=0;
for (int i=2; i<=n; i++)
{
for (int j=1; j<=ok; j++)
{
dp[i][j]=dp[i-1][j];
sum[i][j]=sum[i-1][j];
ll v=prim[j]/gcd(prim[j],bb[i]);
int id=idx[v];
if (dp[i-1][id]+1 < dp[i][j] )
{
dp[i][j]=dp[i-1][id]+1;
sum[i][j]=sum[i-1][id]+aa[i];
}
else if (dp[i-1][id]+1==dp[i][j])
{
if (sum[i][j]>sum[i-1][id]+aa[i])
sum[i][j]=sum[i-1][id]+aa[i];
}
}
}
if (dp
[ok]>n)
{
printf("-1\n");
return 0;
}
printf("%lld\n",dp
[ok]);
tmp=k;
for (int i=n; i>=1; i--)
{
int id=idx[tmp];
if (dp[i][id]==dp[i-1][id]&& sum[i][id]==sum[i-1][id]) continue;
printf("%d ",i);
tmp/=gcd(tmp,bb[i]);
}
printf("\n");
return 0;
}
相关文章推荐
- Codeforces 703E Mishka and Divisors 离散化+DP
- Codeforces Round #365 (Div. 2)Mishka and Divisors 题解。 DP
- E. Mishka and Divisors Codeforces Round #365 (Div. 2) 01背包
- 1354 选数字 DP背包 + 数学剪枝
- CodeChef Cards, bags and coins [DP 泛型背包]
- 周六日常训练,背包dp,树形dp,简单dp以及很多数学?
- QAQ and twin lock [基础dp,数学]
- 背包DP HDOJ 5410 CRB and His Birthday
- hdu 1028/哈理工OJ2004 Ignatius and the Princess III【完全背包】【dp】
- codeforces 703B B. Mishka and trip(数学)
- Mishka and Divisors[CodeForces Round #365 Div.2]
- Codeforces 560E Gerald and Giant Chess 组合数学+DP
- [ARC060]高橋君とカード / Tak and Cards(dp,背包)
- Codeforces Round #280 (Div. 2) E. Vanya and Field (数学GCD)
- bzoj1025: [SCOI2009]游戏[数学分析+背包dp]
- codeforces - 703B - Mishka and trip(数学)
- CodeForces 111B - Petya and Divisors- 暴力-数学
- Codeforces 559C Gerald and Giant Chess 组合数学 DP
- (模拟+数学)codeforces - 703B Mishka and trip
- Chef and Prime Divisors Problem code: CHAPD (GCD问题)