This commit is contained in:
Zengtudor 2025-07-20 09:23:48 +08:00
parent 28022bb787
commit 88b9888340
2 changed files with 185 additions and 13 deletions

View File

@ -1,20 +1,44 @@
#include <cstdint>
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
using ll = int64_t;
ll n,k;
std::vector<ll> a;
using ll = long long;
int main() {
scanf("%ld%ld",&n,&k);
a.resize(n+1);
for(ll i=1;i<=n;i++){
scanf("%ld",&a[i]);
ll n, k;
scanf("%lld %lld", &n, &k);
vector<ll> a(n);
for (int i = 0; i < n; i++) {
scanf("%lld", &a[i]);
}
std::vector<ll> 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<double> 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;
}

148
src/7/20/T635779.md Normal file
View File

@ -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 <algorithm>
#include <cstdio>
#include <vector>
#include <cmath>
#include <cctype>
using namespace std;
using ll = long long;
int main() {
ll n, k;
scanf("%lld %lld", &n, &k);
vector<ll> 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<double> 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.0r为一个较大的值如1e14
- 迭代100次每次计算中点mid = (l + r) / 2.0。
- 使用二分查找确定第一个大于等于mid的位置p计算总使用时间小于mid的风火轮贡献其自身时长大于等于mid的风火轮贡献mid。
- 根据总使用时间是否满足k * mid调整二分边界。
5. **输出结果**:二分结束后,计算中点值作为答案,并保留两位小数输出。
这种方法高效地利用二分搜索和前缀和在O(n log n + 100 log n)的时间复杂度内解决问题,适用于大规模数据。