2020ccpc 威海 Clock Master(分组背包)

在这里插入图片描述
题目大意:
T T T 组数据,每组数据给出一个 n n n ,构造若干个数的和相加不超过 n n n ,即 n ≥ a 1 + a 2 + . . . + a m n \ge a_1+a_2+...+a_m na1+a2+...+am ,求 m a x ( l c m ( a 1 , a 2 , . . . , a m ) ) max(lcm(a_1,a_2,...,a_m)) max(lcm(a1,a2,...,am)) 对结果取以自然对数为底的对数
题目分析:
我们知道一个数 n n n可通过质因数分解表示为 n = p 1 c n t 1 ∗ p 2 c n t 2 ∗ . . . ∗ p x c n t x n=p^{cnt_1}_1*p^{cnt_2}_2*...*p^{cnt_x}_x n=p1cnt1p2cnt2...pxcntx
m m m 个数的 l c m lcm lcm 的本质是将这 m m m 个数质因数分解后相同的质因子指数取最大值相乘,即 l c m = p 1 m a x ( c n t 1 ) ∗ p 2 m a x ( c n t 2 ) ∗ . . . ∗ p y m a x ( c n t y ) lcm=p^{max(cnt_1)}_1*p^{max(cnt_2)}_2*...*p^{max(cnt_y)}_y lcm=p1max(cnt1)p2max(cnt2)...pymax(cnty)
因此要使 l c m lcm lcm 最大为了避免数的浪费就只需将 n n n 拆成多个质数的幂次相加,即 n = p 1 c n t 1 + p 2 c n t 2 + . . . + p m c n t m n = p^{cnt_1}_1+p^{cnt_2}_2+...+p^{cnt_m}_m n=p1cnt1+p2cnt2+...+pmcntm
此时这 m m m 个数的 l c m lcm lcm 即为 p 1 c n t 1 ∗ p 2 c n t 2 ∗ . . . ∗ p m c n t m p^{cnt_1}_1*p^{cnt_2}_2*...*p^{cnt_m}_m p1cnt1p2cnt2...pmcntm
此模型可以理解为每个质数的幂次作为一个组,体积为 p i p^i pi 的数的价值是 l o g ( p i ) log(p^i) log(pi) 然后就可以跑分组背包求解这个最优价值了
求解过程中由于答案很大所以对其取对数处理,利用 l n ( a ∗ b ) = l n a + l n b ln(a*b)=lna+lnb ln(ab)=lna+lnb 避免爆精度
要把3e4内的 d p dp dp 数组直接递推好,询问时 O ( 1 ) O(1) O(1) 查询,此时你会发现你还是 t l e tle tle ,此刻你离成功只差处理一个 l o g log log 值的表,提前打好这个表就可以通过了
时间复杂度为 O ( n ∗ n l o g n ) O(\frac{n*n} {logn}) O(lognnn)
具体细节见代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define ll long long

#define inf 0x3f3f3f3f
using namespace std;
ll read()
{
	ll res = 0,flag = 1;
	char ch = getchar();
	while(ch<'0' || ch>'9')
	{
		if(ch == '-') flag = -1;
		ch = getchar();
	}
	while(ch>='0' && ch<='9')
	{
		res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
		ch = getchar();
	}
	return res*flag;
}
const int maxn = 3e4+5;
const int mod = 1e9+7;
const double pi = acos(-1);
const double eps = 1e-8;
int pri[maxn],cnt;
bool vis[maxn];
void get_prime()//线筛 
{
	vis[1] = true;
	for(int i = 2;i < maxn;i++)
	{
		if(!vis[i]) pri[++cnt] = i;
		for(int j = 1;j <= cnt && i*pri[j] < maxn;j++)
		{
			vis[i*pri[j]] = true;
			if(i%pri[j] == 0) break;
		}
	}
}
double logg[maxn],dp[maxn];
void init()//预处理 
{
	for(int i = 1;i < maxn;i++)
		logg[i] = log(i);
	for(int i = 1;i <= cnt;i++)
			for(int v = maxn-1;v >= pri[i];v--)
				for(int j = pri[i];j <= v;j *= pri[i])
					dp[v] = max(dp[v],dp[v-j]+logg[j]);
}
int main()
{
	get_prime();
	init();
	int t = read();
	while(t--)
	{
		int n = read();
		printf("%.10f\n",dp[n]);
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值