题解:洛谷 P15802 拆分
【题目来源】
洛谷:P15802 [GESP202603 七级] 拆分 - 洛谷
【题目描述】
小 A 想将正整数 \(n\) 拆分成若干个正整数之和,并最大化拆分后的正整数之积。小 A 希望你帮他计算出拆分后正整数之积的最大值。由于答案可能很大,你只需要求出答案对 \(10^9\) 取模的结果。
形式化地,\(n\) 的拆分是满足 \(a_1+\cdots+a_k=n\) 的若干个正整数 \(a_1,\dots,a_k\),其中 \(1\leq k\leq n\)。你需要求出 \(n\) 的所有拆分中 \(a_1\times \cdots\times a_k\) 的最大值对 \(10^9\) 取模的结果。
【输入】
第一行,一个正整数 \(t\),表示数据组数。
对于每组数据:一行,一个整数 \(n\),表示给定的正整数。
【输出】
对于每组数据:输出一行,一个整数,表示 \(n\) 拆分后正整数之积的最大值对 \(10^9\) 取模的结果。
【输入样例】
3
5
8
100
【输出样例】
6
18
755407364
【核心思想】
-
问题分析:给定正整数 \(n\),将其拆分为若干个正整数之和,使得拆分后的乘积最大,结果对 \(10^9\) 取模。这是一个数学贪心 + 快速幂问题,核心在于通过数学分析确定最优拆分策略,避免枚举所有拆分方案。
-
算法选择:
- 数学规律推导:通过均值不等式和函数极值分析,证明拆分出尽可能多的 \(3\) 可使乘积最大
- 快速幂(Binary Exponentiation):在 \(O(\log n)\) 时间内计算 \(3^k \bmod 10^9\)
-
关键步骤:
- 小规模特判:\(n < 6\) 时直接查表:\(ans[1]=1, ans[2]=2, ans[3]=3, ans[4]=4, ans[5]=6\)
- 一般情况分析(\(n \geq 6\)):
- \(n \bmod 3 = 0\):全部分为 \(3\),答案为 \(3^{n/3} \bmod 10^9\)
- \(n \bmod 3 = 1\):将最后一个 \(3+1\) 调整为 \(2+2\)(因为 \(2 \times 2 > 3 \times 1\)),答案为 \(3^{(n/3)-1} \times 4 \bmod 10^9\)
- \(n \bmod 3 = 2\):分为若干个 \(3\) 和一个 \(2\),答案为 \(3^{n/3} \times 2 \bmod 10^9\)
- 快速幂计算:\(qmi(a, b)\) 通过二进制分解指数,在 \(O(\log b)\) 时间内计算 \(a^b \bmod 10^9\)
-
时间/空间复杂度:
- 时间复杂度:\(O(t \cdot \log n)\),每组数据快速幂计算为 \(O(\log n)\)
- 空间复杂度:\(O(1)\),仅使用常数额外空间
-
数学贪心与快速幂的核心思想:
- 最优拆分策略:对于正整数拆分求最大乘积,通过分析函数 \(f(x) = x^{n/x}\) 的极值,可知 \(e \approx 2.718\) 附近最优。在整数范围内,\(3\) 是最接近 \(e\) 的整数,因此尽可能多的拆分为 \(3\) 可使乘积最大
- 余数调整:当 \(n \bmod 3 = 1\) 时,最后一个 \(3+1\) 不如调整为 \(2+2\)(\(4 > 3\)),因此少拆一个 \(3\) 改为两个 \(2\);当 \(n \bmod 3 = 2\) 时,直接保留一个 \(2\)
- 快速幂优化:将指数 \(b\) 二进制分解,通过平方和乘法在 \(O(\log b)\) 时间内完成幂运算,同时每一步取模防止溢出
- 取模运算的分配律:\((a \times b) \bmod m = ((a \bmod m) \times (b \bmod m)) \bmod m\),确保中间结果始终在模数范围内
- 适用于具有数学规律的最优化拆分问题、大数幂运算取模类问题
【解题思路】

【算法标签】
普及 #快速幂
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 4000005, mod = 1e9;
int t, n, ans[6] = {0, 1, 2, 3, 4, 6}; // 小规模n的答案
// 快速幂取模
int qmi(int a, int b)
{
int mul = 1;
while (b)
{
if (b & 1)
{
mul = mul * a % mod;
}
a = a * a % mod;
b >>= 1;
}
return mul;
}
signed main()
{
cin >> t;
while (t--)
{
cin >> n;
if (n < 6) // n较小,直接查表
{
cout << ans[n] << endl;
}
else if (n % 3 == 0) // n是3的倍数
{
cout << qmi(3, n / 3) << endl; // 全部分成3
}
else if (n % 3 == 1) // 余数为1
{
// 将最后一个4分成3+1不如分成2+2,所以是3^(k-1)*4
cout << (qmi(3, n / 3 - 1) * 4) % mod << endl;
}
else // 余数为2
{
// 分成若干个3和一个2
cout << (qmi(3, n / 3) * 2) % mod << endl;
}
}
return 0;
}
【运行结果】
3
5
6
8
18
100
755407364
浙公网安备 33010602011771号