BZOJ 2065: Ural1807 Cartridges for Maxim 【乱搞】

题目描述:

给出一个合数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);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值