1 条题解
-
0
题解:「COCI 2024.3」Rolete
问题分析
题目要求我们为每个查询
h,计算使所有窗帘的下降高度不超过h厘米所需的最短时间。核心思路是枚举手动拉起的窗帘数量,计算对应的最小时间,再取最小值。关键观察
- 预处理排序:将窗帘初始高度
a排序,便于后续分组计算。 - 手动与同步结合:最优策略是先手动拉起部分窗帘(高度最高的
m个),再用同步按钮拉起所有窗帘剩余部分。 - 同步时间计算:同步时,已完全拉起的窗帘会减慢速度。需按拉起高度分段计算时间,利用前缀和优化。
算法步骤
-
排序与预处理:
- 对
a降序排序,得到sorted_a。 - 预处理前缀和数组
pre_sum,其中pre_sum[m]是前m个窗帘的高度和(用于计算手动时间)。 - 预处理分段时间前缀和数组
cost_prefix,其中cost_prefix[x]是同步拉起x厘米的总时间(考虑速度减慢)。
- 对
-
查询处理:
- 对每个查询
h,过滤掉初始高度已满足条件的窗帘。 - 枚举手动拉起的窗帘数量
m(从 0 到剩余窗帘数),计算:- 手动时间:
pre_sum[m] - m * h(需拉起的总高度 × 手动速度t)。 - 同步时间:
cost_prefix[max_h - h](剩余窗帘需拉起的高度 × 同步速度,分段计算)。
- 手动时间:
- 取所有
m对应的总时间最小值,即为答案。
- 对每个查询
数学推导
-
同步时间计算: 同步拉起
x厘米时,速度随已拉起窗帘数r变化:- 第
i段(拉起第i厘米):剩余n - r个窗帘,速度为s + k * r。 - 总时间为各段时间之和:
sum_{i=0}^{x-1} (s + k * min(i, n)) * (n - min(i, n))。 - 分段优化:前
n厘米和超过n厘米的部分分开计算,利用等差数列求和公式。
- 第
-
前缀和优化:
pre_sum[m]快速计算前m个窗帘的手动拉起总高度。cost_prefix[x]预计算同步拉起x厘米的总时间,避免重复计算。
代码实现
n, t, s, k = map(int, input().split()) a = list(map(int, input().split())) a.sort(reverse=True) # 降序排序 # 预处理前缀和 pre_sum[m] = sum_{i=0 to m-1} a[i] pre_sum = [0] * (n + 1) for m in range(1, n + 1): pre_sum[m] = pre_sum[m - 1] + a[m - 1] # 预处理 cost_prefix[x]: 同步拉起 x 厘米的总时间 max_possible_x = max(a) if a else 0 cost_prefix = [0] * (max_possible_x + 2) # 确保足够大 for x in range(1, max_possible_x + 1): if x <= n: # 前 x 厘米,每段 r 从 0 到 x-1,每段时间 (s + k*r) * (n - r) term = (s + k * (x - 1)) * (n - (x - 1)) cost_prefix[x] = cost_prefix[x - 1] + term else: # 超过 n 厘米的部分,每段 r = n,时间 (s + k*n) * 0 = 0 cost_prefix[x] = cost_prefix[x - 1] q = int(input()) h_list = list(map(int, input().split())) ans = [] for h in h_list: # 计算需要处理的窗帘数量:初始高度 > h 的 cnt = 0 while cnt < n and a[cnt] > h: cnt += 1 if cnt == 0: ans.append(0) continue max_h = a[0] # 剩余窗帘的最大初始高度 required = max_h - h # 同步需要拉起的高度 if required < 0: ans.append(0) continue # 枚举手动拉起的数量 m(0 <= m <= cnt) min_time = float('inf') for m in range(0, cnt + 1): # 手动时间:拉起前 m 个窗帘到 h,总高度 (pre_sum[m] - m*h) manual = (pre_sum[m] - m * h) * t if manual < 0: manual = 0 # 避免负数(可能 m*h 超过 pre_sum[m],但实际不需要拉) # 同步时间:拉起 required 厘米 sync = cost_prefix[required] if required <= max_possible_x else cost_prefix[max_possible_x] total = manual + sync if total < min_time: min_time = total ans.append(min_time) print(' '.join(map(str, ans)))复杂度分析
- 预处理:
- 排序:
O(n log n)。 - 前缀和
pre_sum:O(n)。 - 同步时间前缀和
cost_prefix:O(max_a)(max_a是窗帘最大初始高度,<= 1e5)。
- 排序:
- 查询处理:
- 每个查询枚举
m的数量为O(cnt)(cnt是初始高度 > h 的窗帘数,最坏O(n))。 - 总查询复杂度:
O(q * n),但实际中cnt较小,且通过排序优化了枚举效率。
- 每个查询枚举
优化方向
- 对于
q和n较大的情况(如 1e5),枚举m可能超时。可进一步优化为二分查找m的最优值,将查询复杂度降为O(q log n)。 - 预计算
cost_prefix时,可直接用数学公式计算分段和,避免循环(适用于max_a较大的场景)。
总结
本题的核心是枚举手动拉起的窗帘数量,结合预处理的前缀和数组快速计算手动和同步时间,最终取最小值。通过排序和前缀和优化,确保了算法的高效性,能够应对题目中的数据规模。
- 预处理排序:将窗帘初始高度
- 1
信息
- ID
- 5587
- 时间
- 2000ms
- 内存
- 512MiB
- 难度
- 8
- 标签
- 递交数
- 6
- 已通过
- 1
- 上传者