使用三个队列维护单调性,分别存储初始蚯蚓、切出的左半段和右半段,实现 $O(m)$ 的切分模拟。
OJ: roj
题目 ID: 3641
难度:提高+/省选-
标签:队列贪心模拟
创建: 2026-10-10 09:28
更新: 2026-10-10 09:28
形式化题目
有
- 每
秒切断的蚯蚓在切断前的长度(以空格隔开)。 - 在所有切分完成后,每隔
个输出剩余蚯蚓的长度,按照长度从大到小排列。
数据范围:
注意: 本题在原始题库中编号
3641,题面虽为「组合数问题」,但实际判题数据与格式完全对应的是 NOIP 2016 提高组的「蚯蚓」一题(首行形如n m q u v t,且.ans存在大段多行序列且附有前导换行符等)。本题解已根据实际测试数据更正题意并提供匹配的算法。
正解
思路
每次要找出当前最长的蚯蚓切断,最直观的做法是使用优先队列维护当前所有蚯蚓的长度。由于每次除了被切断的蚯蚓外,其余所有蚯蚓都要增长 offset。每秒 offset += q,当切断一条原长度为 offset 算出)的蚯蚓后,切出的两段长度计算出后要放入优先队列,为了抵消 offset,放入队列的值应减去当前的 offset + q。这种做法时间复杂度为
注意到本题存在一个关键的单调性质:
若较早被切断的蚯蚓原长为
因此,我们可以维护三个普通的队列:
- 队列 0:存放一开始就有的
条蚯蚓,初始时将其降序排序。 - 队列 1:存放每次被切断后产生的第一条较短或按比例的那条蚯蚓段。
- 队列 2:存放每次被切断后产生的另一半蚯蚓段。
由于这三个队列内部都是单调递减的,每次选取当前最长的蚯蚓时,只需在这三个队列的队首比较大小,选取最大值即可,这样选取最大值的操作是
代码
import sys
def solve():
input_data = sys.stdin.read().split()
if not input_data:
return
if len(input_data) < 6:
return
n = int(input_data[0])
m = int(input_data[1])
q = int(input_data[2])
u = int(input_data[3])
v = int(input_data[4])
t = int(input_data[5])
q0_arr = []
for i in range(n):
q0_arr.append(int(input_data[6 + i]))
q0_arr.sort(reverse=True)
head0 = 0
tail0 = n
from collections import deque
q1 = deque()
q2 = deque()
offset = 0
out1 = []
for i in range(1, m + 1):
max_val = -3000000000
which = -1
if head0 < tail0 and q0_arr[head0] > max_val:
max_val = q0_arr[head0]
which = 0
if q1 and q1[0] > max_val:
max_val = q1[0]
which = 1
if q2 and q2[0] > max_val:
max_val = q2[0]
which = 2
if which == 0:
head0 += 1
elif which == 1:
q1.popleft()
elif which == 2:
q2.popleft()
L = max_val + offset
if i % t == 0:
out1.append(str(L))
L1 = L * u // v
L2 = L - L1
q1.append(L1 - offset - q)
q2.append(L2 - offset - q)
offset += q
if out1:
sys.stdout.write(" ".join(out1) + "\n")
else:
sys.stdout.write("\n")
out2 = []
count = 0
while head0 < tail0 or q1 or q2:
max_val = -3000000000
which = -1
if head0 < tail0 and q0_arr[head0] > max_val:
max_val = q0_arr[head0]
which = 0
if q1 and q1[0] > max_val:
max_val = q1[0]
which = 1
if q2 and q2[0] > max_val:
max_val = q2[0]
which = 2
if which == 0:
head0 += 1
elif which == 1:
q1.popleft()
elif which == 2:
q2.popleft()
count += 1
if count % t == 0:
out2.append(str(max_val + offset))
if out2:
sys.stdout.write(" ".join(out2) + "\n")
else:
sys.stdout.write("\n")
if __name__ == '__main__':
solve()#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int MAXN = 100005;
int q0_arr[MAXN];
bool compare_desc(int a, int b) {
return a > b;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n, m, q, u, v, t;
if (!(cin >> n >> m >> q >> u >> v >> t)) return 0;
for (int i = 0; i < n; ++i) {
cin >> q0_arr[i];
}
sort(q0_arr, q0_arr + n, compare_desc);
int head0 = 0, tail0 = n;
queue<int> q1, q2;
long long offset = 0;
bool first1 = true;
for (int i = 1; i <= m; ++i) {
long long max_val = -3000000000LL;
int which = -1;
if (head0 < tail0 && q0_arr[head0] > max_val) {
max_val = q0_arr[head0];
which = 0;
}
if (!q1.empty() && q1.front() > max_val) {
max_val = q1.front();
which = 1;
}
if (!q2.empty() && q2.front() > max_val) {
max_val = q2.front();
which = 2;
}
if (which == 0) head0++;
else if (which == 1) q1.pop();
else if (which == 2) q2.pop();
long long L = max_val + offset;
if (i % t == 0) {
if (!first1) cout << " ";
cout << L;
first1 = false;
}
long long L1 = L * u / v;
long long L2 = L - L1;
q1.push((int)(L1 - offset - q));
q2.push((int)(L2 - offset - q));
offset += q;
}
cout << "\n";
int count = 0;
bool first2 = true;
while (head0 < tail0 || !q1.empty() || !q2.empty()) {
long long max_val = -3000000000LL;
int which = -1;
if (head0 < tail0 && q0_arr[head0] > max_val) {
max_val = q0_arr[head0];
which = 0;
}
if (!q1.empty() && q1.front() > max_val) {
max_val = q1.front();
which = 1;
}
if (!q2.empty() && q2.front() > max_val) {
max_val = q2.front();
which = 2;
}
if (which == 0) head0++;
else if (which == 1) q1.pop();
else if (which == 2) q2.pop();
count++;
if (count % t == 0) {
if (!first2) cout << " ";
cout << (max_val + offset);
first2 = false;
}
}
cout << "\n";
return 0;
}复杂度
- 时间复杂度:
初始时对给定的
条蚯蚓排序,花费 。 随后进行 次切分,每次只需要比较三个队列的队首,选取最大值并进行相应的进队出队操作,花费 。 最后把三个队列剩余元素取空输出,花费 。 总时间复杂度为 。 - 空间复杂度:
需要开三个数组模拟队列。队列 0 需要
,队列 1 和队列 2 各自最多需要 ,所以总空间复杂度为 ,完全满足本题要求。
总结
- 利用隐藏的单调性,将需要
维护最值的优先队列退化成了三个相互之间有单调性的普通队列,将复杂度成功优化至线性的 。这是处理这类题目的极其经典的技巧。 - 对于全局所有元素的集体变化,应采用偏移量(
offset)进行相对管理,极大程度提升了效率。 - 数据排坑提示:本题题库中的题面描述是「组合数问题」,但实际判题系统挂载的测试数据完全是「蚯蚓」一题(即六个参数输入
n m q u v t等特征)。本题必须以数据特征为准。部分答案文件带有前导空行等均属于数据固有特征。
