乘积最大 3 的题解
在解题之前提交题解的代码会导致封禁。
作者:
概述
本题要求把正整数 拆成
个正整数之和,在乘积尽可能大的前提下输出字典序最小的方案。核心解法是均值原理:最优方案中任意两数之差不超过
,即所有元素只取
与
两种值,各自的个数由总和唯一确定,按非降序排列即为字典序最小的方案。
分析
核心观察
若方案中存在两数之差大于等于 ,把较大的数减
、较小的数加
,总和不变而乘积严格变大。因此乘积最大的方案中任意两数之差不超过
,所有数都紧贴平均值。
思路
设方案为 ,其和为
。若存在两个元素满足
,把它们替换为
与
,总和保持不变,而乘积的变化量为
乘积严格增大,与方案的最优性矛盾。故最优方案中任意两数之差不超过 ,所有元素只可能是平均值的下取整或上取整。记
则每个元素只能取 或
。设取
的元素个数为
,由总和相等得到
可见取 的个数恰好等于余数
,其余
个元素取
。因此乘积最大的方案所对应的多重集是完全确定的:由
个
与
个
构成。
多重集确定后,剩下的只是排列顺序问题。字典序比较从第一个元素开始,把更小的数放在前面不会更劣;而任意一对逆序相邻的元素 交换为
都会使序列的字典序变小。因此把全部
排在前面、全部
排在后面(即非降序排列)即为字典序最小的方案。
若改用枚举所有拆分或动态规划来求最大乘积,状态规模随拆分数目增长,在 、
时完全不可行;本题只需按上述公式直接构造答案。需要注意输出本身就包含
个数,任何做法的耗时都不会低于
。
具体示例
以 、
为例,
,
,故方案由
个
与
个
构成。三种排列中
与
的字典序都更大,故输出
。
再以 、
为例,
,
,方案由
个
与
个
构成,输出
。若换成
个
与
个
,总和仍为
,但最大值与最小值之差为
,乘积更小。
当 能被
整除时
,全部
个数都取
,例如
、
输出
;当
时输出
本身。
算法步骤
- 读入两个整数,分别存入
n与m。 - 计算
q与
r。
- 依次输出
个
q,再输出r个,相邻两数之间用一个空格分隔,最后一个数输出后换行。
复杂度分析
- 时间:
。其中计算
与
为
,其余开销全部来自输出
个数。
- 空间:
的额外空间(不计输出缓冲)。若实现时先把答案拼接为字符串,则需
的字符空间。
实现注意事项
- 输出顺序必须先小后大,即先输出
再输出
;把
放在前面会得到字典序更大的方案。
- 每组输入对应的输出是单行、
个整数、以单个空格分隔、行末换行,且行末不留多余空格;用「除第一个数外每次先输出一个空格」的写法可以自然满足该格式。
- 数据保证
,故
,不会出现
或负数,无需特判;
时全部
个数均为
。
- 不要真正计算乘积:最大乘积约为
量级,远超
位整数范围。本题只需构造方案,不需求出乘积的值。
- 参与运算的量本身很小:
,
,
位整数即可容纳,C++ 中使用 long long 同样安全。
- 输出规模最大约
字节,C++ 需关闭流同步并使用 '\n' 而非 endl,避免每次输出都刷新缓冲区;Python 应把结果拼接成一个字符串后一次性输出,避免逐个数调用 print。
- 输入只有一行两个整数,注意一次性读入全部数据,不要因按行读取而只读到第一个数。
源代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
long long n, m;
cin >> n >> m;
long long q = n / m;
long long r = n % m;
for (long long i = 0; i < m; ++i) {
if (i > 0) {
cout << ' ';
}
cout << (i < m - r ? q : q + 1);
}
cout << '\n';
return 0;
}
import sys
def main():
n, m = map(int, sys.stdin.read().split())
q, r = divmod(n, m)
ans = [str(q)] * (m - r) + [str(q + 1)] * r
sys.stdout.write(' '.join(ans) + '\n')
main()
评论