题目描述:
给出一个合数n,找出一些数字a1,a2,…,ak使得a1+a2+…+ak=n,且gcd(a1,a2,…,ak)最大,如果有多种可能再使lcm(a1,a2,…,ak)最大,升序输出a1,a2,…,ak。
200 <= n <= 109
题目分析:
因为a1+a2+…+ak=n,所以gcd(a1,a2,…,ak)一定是n的约数,那么最大的时候就是n的最大因数
那么a1,a2…ak除去gcd后剩下部分的和就是n的最小因子m,可以确定m<=sqrt(n)<32000
因为要lcm最大,所以这几个数必然互质(ai可以为1)
那么问题转化为:给一个K<32000,把K分为互质的几部分,使得这几部分的积最大
考虑枚举每一个素数做背包,
f
[
i
]
f[i]
f[i]表示用和为i的数得到的最大的积
这样做虽然很慢,但是能过。。。
经过测试,实际上32000以内的数只会用到700以内的素数,所以素数枚举可以大大减少。。。
记录一下路径最后输出就可以了
注意两点,m=2和m=3的时候背包只会选出一个数,需要特判
还有
f
[
i
]
f[i]
f[i]会乘炸,改成用对数相加
BZOJ只有几个人做??
#include<cstdio>
#include<cmath>
#include<algorithm>
#define maxn 32001
using namespace std;
int n,m,pre[maxn][2];
int p[maxn],num,a[maxn],cnt;
bool v[maxn];
double f[maxn];
void init(int N)
{
for(int i=2;i<=700;i++){//这里的700是试出来的
if(!v[i]) p[++num]=i;
for(int j=i+i;j<=700;j+=i) v[j]=1;
}
for(int i=1;i<=N;i++) pre[i][0]=1,pre[i][1]=1;
for(int i=1;i<=num;i++){
double lx=log(p[i]);int y=p[i];
for(int j=N;j>=y;j--)
{
int x=y;double s=lx;
for(int k=1;x<=j;x*=p[i],s+=lx,k++)
if(f[j-x]+s>f[j]) f[j]=f[j-x]+s,pre[j][0]=p[i],pre[j][1]=k;
}
}
}
int main()
{
scanf("%d",&n);
for(m=2;;m++) if(n%m==0) break;
init(m),n/=m;
if(m==2) return printf("%d %d",n,n),0;
if(m==3) return printf("%d %d",n,n*2),0;
while(m) a[++cnt]=round(pow(pre[m][0],pre[m][1])),m-=a[cnt];
sort(a+1,a+1+cnt);
for(int i=1;i<=cnt;i++) printf("%d%c",a[i]*n,i==cnt?10:32);
}