题解:洛谷 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

【核心思想】

  1. 问题分析:给定正整数 \(n\),将其拆分为若干个正整数之和,使得拆分后的乘积最大,结果对 \(10^9\) 取模。这是一个数学贪心 + 快速幂问题,核心在于通过数学分析确定最优拆分策略,避免枚举所有拆分方案。

  2. 算法选择

    • 数学规律推导:通过均值不等式和函数极值分析,证明拆分出尽可能多的 \(3\) 可使乘积最大
    • 快速幂(Binary Exponentiation):在 \(O(\log n)\) 时间内计算 \(3^k \bmod 10^9\)
  3. 关键步骤

    • 小规模特判\(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\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(t \cdot \log n)\),每组数据快速幂计算为 \(O(\log n)\)
    • 空间复杂度:\(O(1)\),仅使用常数额外空间
  5. 数学贪心与快速幂的核心思想

    • 最优拆分策略:对于正整数拆分求最大乘积,通过分析函数 \(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
posted @ 2026-07-09 23:26  团爸讲算法  阅读(10)  评论(0)    收藏  举报