题目链接
题目描述
定义一个正整数 的权值
为其正因子的数量。现在给定一个正整数
,你可以将其分解为若干个大于
的正整数的乘积,即
。你的目标是最大化这些因子的权值之和,即最大化
。
解题思路
本题要求找到一种分解方式,使得所有因子的权值(因子数量)之和最大。由于输入 的范围不大(
),我们可以采用动态规划并预处理出所有可能答案。
-
问题分析与DP递推:对于一个数
,我们可以选择不分解它,得到权值
;或者将它分解。一个关键的洞察是,任何分解都可以看作是不断分离出质因子的过程。为了构建最优解,我们只需要考虑分离出最小质因子 (Smallest Prime Factor, SPF) 的情况。
- 定义状态:设
是将数字
进行最优分解后能获得的最大权值和。
- 状态转移方程:对于任何合数
,其最优解是在“不分解”和“分离出最小质因子
”这两种策略中取最优。
- 不分解:价值为
。
- 分解:价值为
。由于
是质数,无法再分,
dp[p]
就是wt[p]=2
。因此分解的价值是。
- 最终递推式为:
。
- 不分解:价值为
- 定义状态:设
-
预处理步骤:
- 预计算
wt
数组:使用的筛法计算从
到
每个数的因子数量。
- 预计算
spf
数组:使用的线性筛法变体,计算从
到
每个数的最小质因子。
- 计算
dp
数组:- 初始化
dp[i] = wt[i]
。 - 使用一个单重循环,从
i = 2
到,根据上述递推式
dp[i] = max(dp[i], 2 + dp[i/spf[i]])
计算出所有dp
值。因为计算dp[i]
时,dp[i/spf[i]]
的值已经计算完毕,所以这个顺序是可行的。
- 初始化
- 预计算
-
处理查询:完成预处理后,对于每个查询
,答案就是
dp[x]
。
代码
#include <iostream>
#include <vector>
#include <cmath>
#include <numeric>
using namespace std;
const int MAX_X = 200005;
long long wt[MAX_X];
int spf[MAX_X];
long long dp[MAX_X];
void precompute() {
for (int i = 1; i < MAX_X; ++i) {
wt[i] = 0;
spf[i] = i;
}
for (int i = 1; i < MAX_X; ++i) {
for (int j = i; j < MAX_X; j += i) {
wt[j]++;
}
}
for (int i = 2; i * i < MAX_X; ++i) {
if (spf[i] == i) { // i is a prime
for (int j = i * i; j < MAX_X; j += i) {
if (spf[j] == j) {
spf[j] = i;
}
}
}
}
dp[1] = 0;
for (int i = 2; i < MAX_X; ++i) {
dp[i] = wt[i];
if (spf[i] < i) { // i is composite
dp[i] = max(dp[i], dp[spf[i]] + dp[i / spf[i]]);
}
}
}
void solve() {
int x;
cin >> x;
cout << dp[x] << endl;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
precompute();
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
import java.util.Scanner;
public class Main {
static final int MAX_X = 200005;
static long[] wt = new long[MAX_X];
static int[] spf = new int[MAX_X];
static long[] dp = new long[MAX_X];
static {
for (int i = 1; i < MAX_X; i++) {
wt[i] = 0;
spf[i] = i;
}
for (int i = 1; i < MAX_X; i++) {
for (int j = i; j < MAX_X; j += i) {
wt[j]++;
}
}
for (int i = 2; i * i < MAX_X; i++) {
if (spf[i] == i) { // i is a prime
for (int j = i * i; j < MAX_X; j += i) {
if (spf[j] == j) {
spf[j] = i;
}
}
}
}
dp[1] = 0;
for (int i = 2; i < MAX_X; i++) {
dp[i] = wt[i];
if (spf[i] < i) { // i is composite
dp[i] = Math.max(dp[i], dp[spf[i]] + dp[i / spf[i]]);
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
while (t-- > 0) {
int x = sc.nextInt();
System.out.println(dp[x]);
}
}
}
import sys
MAX_X = 200001
wt = [0] * MAX_X
spf = [i for i in range(MAX_X)]
dp = [0] * MAX_X
def precompute():
for i in range(1, MAX_X):
for j in range(i, MAX_X, i):
wt[j] += 1
i = 2
while i * i < MAX_X:
if spf[i] == i: # i is prime
for j in range(i * i, MAX_X, i):
if spf[j] == j:
spf[j] = i
i += 1
dp[1] = 0
for i in range(2, MAX_X):
dp[i] = wt[i]
if spf[i] < i: # i is composite
dp[i] = max(dp[i], dp[spf[i]] + dp[i // spf[i]])
def main():
precompute()
t_str = sys.stdin.readline()
if not t_str: return
t = int(t_str)
for _ in range(t):
x_str = sys.stdin.readline()
if not x_str: continue
x = int(x_str)
print(dp[x])
if __name__ == "__main__":
main()
算法及复杂度
- 算法:动态规划、预处理、筛法
- 时间复杂度:预处理
wt
数组的复杂度为,预处理
spf
数组的复杂度为,计算
dp
数组的复杂度为。总时间复杂度由预处理主导,为
。每次查询的复杂度为
。
- 空间复杂度:需要三个数组
wt
,spf
,dp
来存储预处理的结果,空间复杂度为。