From 88b988834074fa2897bc33c20d1f821eecccb3cf Mon Sep 17 00:00:00 2001 From: Zengtudor Date: Sun, 20 Jul 2025 09:23:48 +0800 Subject: [PATCH] update --- src/7/20/T635779.cpp | 50 +++++++++++---- src/7/20/T635779.md | 148 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 185 insertions(+), 13 deletions(-) create mode 100644 src/7/20/T635779.md diff --git a/src/7/20/T635779.cpp b/src/7/20/T635779.cpp index 7ab80b9..19aec78 100644 --- a/src/7/20/T635779.cpp +++ b/src/7/20/T635779.cpp @@ -1,20 +1,44 @@ -#include +#include #include #include +using namespace std; -using ll = int64_t; +using ll = long long; -ll n,k; -std::vector a; - -int main(){ - scanf("%ld%ld",&n,&k); - a.resize(n+1); - for(ll i=1;i<=n;i++){ - scanf("%ld",&a[i]); +int main() { + ll n, k; + scanf("%lld %lld", &n, &k); + vector a(n); + for (int i = 0; i < n; i++) { + scanf("%lld", &a[i]); } - std::vector vk(k+1); - for(ll i=1;i<=k;i++){ + + if (k > n) { + printf("0.00\n"); + return 0; + } + + sort(a.begin(), a.end()); + vector p(n + 1, 0.0); + for (int i = 0; i < n; i++) { + p[i + 1] = p[i] + a[i]; + } + + double l = 0.0, r = 1e14; + for (int iter = 0; iter < 100; iter++) { + double mid = (l + r) / 2.0; + auto it = lower_bound(a.begin(), a.end(), mid); + int pos = it - a.begin(); - } + double sum = p[pos] + (n - pos) * mid; + if (sum >= k * mid) { + l = mid; + } else { + r = mid; + } + } + + double ans = (l + r) / 2.0; + printf("%.2f\n", ans); + return 0; } \ No newline at end of file diff --git a/src/7/20/T635779.md b/src/7/20/T635779.md new file mode 100644 index 0000000..d73274a --- /dev/null +++ b/src/7/20/T635779.md @@ -0,0 +1,148 @@ +你的代码试图通过模拟风火轮的使用过程来求解最大时长,但存在几个关键问题: + +### 问题分析 +1. **错误的理解问题**:题目要求的是同时使用k个风火轮的**最大连续时长**,而不是累计使用时间。你的代码模拟了分段使用风火轮的过程,计算的是所有时间段的总和,这不符合题目要求。 +2. **模拟方法错误**:你的模拟过程(每次取出最小值,累计时间,然后更新集合)实际上计算的是所有风火轮分段使用时的总时间,而不是同时使用k个风火轮的最大连续时长。 +3. **输出格式问题**:题目要求结果四舍五入保留2位小数,但你的代码直接输出`float(ans)`,没有进行格式化输出。 + +### 正确方法 +问题本质是求最大时间t,使得满足: +``` +∑ min(a_i, t) ≥ k * t +``` +其中: +- 当风火轮时长a_i ≥ t时,贡献为t +- 当a_i < t时,贡献为a_i + +这可以通过**二分答案**解决: +1. 对风火轮时长数组排序 +2. 计算前缀和数组 +3. 二分查找最大t值 +4. 对于每个mid,用前缀和快速计算∑ min(a_i, mid) + +### 修复后的代码 + +```cpp +#include +#include +#include +#include +#include +using namespace std; + +using ll = long long; + +int main() { + ll n, k; + scanf("%lld %lld", &n, &k); + vector a(n); + for (int i = 0; i < n; i++) { + scanf("%lld", &a[i]); + } + + // 如果k > n,无法同时使用k个风火轮 + if (k > n) { + printf("0.00\n"); + return 0; + } + + sort(a.begin(), a.end()); + vector pref(n + 1, 0.0); + for (int i = 0; i < n; i++) { + pref[i + 1] = pref[i] + a[i]; + } + + double l = 0.0, r = 1e14; + for (int iter = 0; iter < 100; iter++) { + double mid = (l + r) / 2.0; + // 找到第一个大于等于mid的位置 + auto it = lower_bound(a.begin(), a.end(), mid); + int pos = it - a.begin(); + + double total = pref[pos] + (n - pos) * mid; + if (total >= k * mid) { + l = mid; + } else { + r = mid; + } + } + + double ans = (l + r) / 2.0; + printf("%.2f\n", ans); + return 0; +} +``` + +### 关键改进说明 +1. **二分答案**:使用二分查找确定最大连续时长t +2. **前缀和优化**: + - 对排序后的数组计算前缀和 + - 对于每个mid值: + - 使用`lower_bound`找到第一个≥mid的位置 + - 小于mid的部分直接取原值(前缀和) + - ≥mid的部分取mid值 +3. **边界处理**:当k>n时直接输出0.00 +4. **精度控制**:二分迭代100次保证精度 +5. **正确输出**:使用`%.2f`格式输出,自动四舍五入 + +这个解法时间复杂度为O(n log n + 100 log n),可以高效处理最大10^5规模的数据。 + +为了解决这个问题,我们需要确定同时使用k个风火轮的最大时长,使得在任意时刻都有k个风火轮在转动,并且可以随时更换风火轮(更换时间忽略不计)。问题的核心在于找到一个最大的时间t,使得所有风火轮的总使用时间至少为k * t。 + +### 方法思路 +1. **问题分析**:我们需要找到一个最大的时间t,使得所有风火轮的使用时间之和至少为k * t。每个风火轮的使用时间不能超过其自身可旋转时长,也不能超过t(因为使用时间达到t后需要更换)。 +2. **关键观察**:对于给定的时间t,每个风火轮i的贡献是min(a_i, t)。因此,总使用时间是所有min(a_i, t)的和。我们需要满足sum(min(a_i, t)) ≥ k * t。 +3. **二分搜索**:由于t的取值范围很大(0到最大可能值),我们使用二分搜索来高效地找到满足条件的最大t。二分搜索的每一步中,我们计算当前mid值对应的总使用时间,并与k * mid比较。 +4. **优化计算**:为了快速计算sum(min(a_i, t)),我们首先对风火轮的可旋转时长数组排序,并计算前缀和。然后使用二分查找确定有多少个风火轮的时长小于mid,从而将总使用时间分为两部分:小于mid的部分直接求和,大于等于mid的部分贡献mid。 +5. **边界处理**:如果k大于风火轮数量n,则不可能同时使用k个风火轮,结果直接为0.00。 + +### 解决代码 +```python +import bisect + +def main(): + import sys + data = sys.stdin.read().split() + n = int(data[0]) + k = int(data[1]) + a = list(map(int, data[2:2+n])) + + if k > n: + print("0.00") + return + + a.sort() + pref = [0] * (n+1) + for i in range(n): + pref[i+1] = pref[i] + a[i] + + l, r = 0.0, 10**14 + + for _ in range(100): + mid = (l + r) / 2.0 + p = bisect.bisect_left(a, mid) + total = pref[p] + (n - p) * mid + if total >= k * mid: + l = mid + else: + r = mid + + ans = (l + r) / 2.0 + print("{:.2f}".format(ans)) + +if __name__ == "__main__": + main() +``` + +### 代码解释 +1. **输入处理**:读取输入数据,包括风火轮数量n、需要同时使用的风火轮数量k,以及每个风火轮的可旋转时长列表a。 +2. **边界检查**:如果k大于n,直接输出0.00,因为无法同时使用k个风火轮。 +3. **排序和前缀和**:对风火轮的可旋转时长列表进行排序,并计算前缀和数组pref,用于快速计算子数组的和。 +4. **二分搜索**: + - 初始化二分边界l为0.0,r为一个较大的值(如1e14)。 + - 迭代100次,每次计算中点mid = (l + r) / 2.0。 + - 使用二分查找确定第一个大于等于mid的位置p,计算总使用时间:小于mid的风火轮贡献其自身时长,大于等于mid的风火轮贡献mid。 + - 根据总使用时间是否满足k * mid调整二分边界。 +5. **输出结果**:二分结束后,计算中点值作为答案,并保留两位小数输出。 + +这种方法高效地利用二分搜索和前缀和,在O(n log n + 100 log n)的时间复杂度内解决问题,适用于大规模数据。 \ No newline at end of file