题意
给你一个正整数
n
n
n,你需要将
n
n
n拆成若干个正整数的和。设拆成了
t
1
,
t
2
,
.
.
.
,
t
s
{t_1,t_2,...,t_s}
t1,t2,...,ts这
s
s
s个正整数,对于任意自然数
k
k
k,存在向量
(
k
(k
(k
m
o
d
mod
mod
t
1
,
{t_1},
t1,
k
k
k
m
o
d
mod
mod
t
2
,
.
.
.
,
{t_2},...,
t2,...,
k
k
k
m
o
d
mod
mod
t
s
)
{t_s})
ts),这些向量中的不同向量个数记为
x
x
x。
因此每种拆分对应一个不同的向量个数,现在让你求一个使
x
x
x最大的拆分,输出
ln
x
\ln x
lnx。
数据范围: 1 ⩽ n ⩽ 3 × 1 0 4 1\leqslant n\leqslant 3\times10^4 1⩽n⩽3×104
思路
① 一个结论:
x
=
l
c
m
(
t
1
,
t
2
,
.
.
.
,
t
s
)
x=lcm(t_1,t_2,...,t_s)
x=lcm(t1,t2,...,ts)
由于本文主要侧重分组背包部分,对这个结论感兴趣的读者可以自己到网上寻找证明。
② 接下来就是考虑怎么样拆分。考虑最优解一定存在拆成的数两两互质的形式,多余的部分都可以拆成若干个
1
1
1来凑数(
1
1
1与任何数互质),这样计算答案就变成了计算
ln
t
1
+
ln
t
2
+
.
.
.
+
ln
t
s
\ln t_1+\ln t_2+...+\ln t_s
lnt1+lnt2+...+lnts。
证明: 假设最优解为
t
1
,
t
2
,
.
.
.
,
t
s
{t_1,t_2,...,t_s}
t1,t2,...,ts
1、若这些数两两互质,则成立。
2、若
g
c
d
(
t
x
,
t
y
)
=
d
>
=
2
gcd(t_x,t_y)=d>=2
gcd(tx,ty)=d>=2,则将
t
x
t_x
tx从解中移除,将
t
x
/
d
t_x/d
tx/d 和
t
x
−
t
x
/
d
t_x-t_x/d
tx−tx/d 个
1
1
1加入到解中,原解的值不变。
3、重复步骤2直到所有数两两互质。
③考虑拆成若干个质数的幂次形式,即每个
t
t
t都是
p
i
{p^i}
pi的形式,其中
p
p
p为质数,
i
i
i为自然数。
一个
p
i
{p^i}
pi的价值是
ln
p
i
\ln {p^i}
lnpi,这不就是背包吗。
④对于每个
p
p
p,只能选一次
p
i
{p^i}
pi,但不同的质数是可以任意选的,因此做一个分组背包,先筛一下质数,然后按
p
p
p分组。预处理答案然后
O
(
1
)
O(1)
O(1)回答。
⑤总时间复杂度为
O
(
n
O(n
O(n
∑
i
=
2
,
i
为
质
数
n
log
i
n
\sum_{i=2,i为质数}^n \log_i n
∑i=2,i为质数nlogin
)
)
) ,
ln
t
\ln t
lnt需要预处理不然会T。
#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dep(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int maxn=4e4+5;
const double ln = log(exp(1));
template <typename T>
inline void read(T &X){
X=0;int w=0; char ch=0;
while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
if(w) X=-X;
}
int n,m,k,cnt;
int a[maxn],c[maxn];
double dp[maxn];
double ans[maxn],lg[maxn];
int flag;
void init(){
rep(i,2,30000){
if(!c[i]){
c[++cnt] = i;
for (int j = i * 2; j <= 30000;j+=i)
c[j] = 1;
}
lg[i] = log(i) / ln;
}
}
void solve(){
n = 30000;
rep(i,1,cnt){
dep(j,n,c[i]){
ll tp = c[i];
while(tp<=j){
dp[j] = max(dp[j], dp[j - tp] + lg[tp]);
tp *= c[i];
}
}
}
rep(i, 2, n) ans[i] = max(ans[i - 1], dp[i]);
}
int main(){
init();
solve();
int T=1,cas=1;
read(T);
while (T--){
read(n);
cout << fixed << setprecision(12) << ans[n] << endl;
}
return 0;
}