题目大意:
T
T
T 组数据,每组数据给出一个
n
n
n ,构造若干个数的和相加不超过
n
n
n ,即
n
≥
a
1
+
a
2
+
.
.
.
+
a
m
n \ge a_1+a_2+...+a_m
n≥a1+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=p1cnt1∗p2cnt2∗...∗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
p1cnt1∗p2cnt2∗...∗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(a∗b)=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(lognn∗n)
具体细节见代码:
#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;
}