【转载】【队列】【数学】[NOIP2016]蚯蚓
对于这样一个题,最简单的想法就是用堆来模拟m次切割的过程,不过时间复杂度为O((n+m)log(n+m))(要用楼下所讲的进行优化),只能通过65%的数据。
https://ac.nowcoder.com/acm/contest/22669/J
make_pair的对于三个元素比较大小并且获得最大值的骚操作可以学习一下
原作者:dbxxx
题解思路
基于堆的做法(O(mlogm))
当 ( q = 0 ) 时,使用 ( O(m \log m) ) 的堆可以解决问题。
当 ( q > 0 ) 时,直接暴力地对集合中的每个元素加上 ( q ) 是不可取的。观察到,除了被切开的两个元素不 ( +q ),其余元素均 ( +q )。因此可以等效地理解为:将 ( x ) 切分为 ( \lfloor px \rfloor - q ) 和 ( x - \lfloor px \rfloor - q ),然后对所有集合中的元素进行偏移。
在这种情况下,我们可以每秒记录集合通过全局偏移量 ( +q ),从而优化过程。偏移量在 ( t ) 秒后是 ( qt ),因此可以在 ( O(m \log m) ) 的时间复杂度下解决本题。
算法步骤
-
循环 ( t ) 从 ( 0 ) 到 ( m-1 ):
- 取出集合的最大值 ( x' ),计算真实值 ( x = x' + qt );
- 将 ( x ) 切分为 ( \lfloor px \rfloor ) 和 ( x - \lfloor px \rfloor ) 两部分;
- 分别将 ( \lfloor px \rfloor - q - qt ) 和 ( x - \lfloor px \rfloor - q - qt ) 放回集合中。
-
通过上述方法解决问题,但该方法的效率不够优秀。
更优的做法(O(m))
在 ( q = 0 ) 时,可以维护三个队列 A、B、C,其中:
- A 保存原始集合,从大到小排列;
- B 保存每次切分生成的 ( \lfloor px \rfloor );
- C 保存每次切分生成的 ( x - \lfloor px \rfloor )。
队列 A、B、C 保持单调性,从而取最大值的操作仅需比较三个队列的队首元素。每次操作只需选择最大值的队首,切分后将新元素推入 B 或 C 队尾即可。
对于 ( q \geq 0 ) 的情况
假设在某一秒切分数 ( x_1 ),下一秒切分数为 ( x_2 + q ),有 ( x_1 \geq x_2 )。需要证明:
- ( \lfloor px_1 \rfloor + q \geq \lfloor p(x_2 + q) \rfloor )
- ( x_1 - \lfloor px_1 \rfloor + q \geq x_2 + q - \lfloor p(x_2 + q) \rfloor )
该做法有效,时间复杂度为 ( O(m) )。
代码实现
#include <bits/stdc++.h>
inline int read() {
int x = 0;
bool f = true;
char ch = getchar();
for (; !isdigit(ch); ch = getchar())
if (ch == '-') f = false;
for (; isdigit(ch); ch = getchar())
x = (x << 1) + (x << 3) + ch - '0';
return f ? x : (~(x - 1));
}
const int maxn = (int)1e5 + 5;
const int mininf = 0xc0c0c0c0;
int a[maxn];
std::queue<int> qw[4];
typedef std::pair<int, int> pii;
int main() {
int n = read(), m = read(), q = read(), u = read(), v = read(), t = read();
for (int i = 1; i <= n; ++i) a[i] = read();
std::sort(a + 1, a + n + 1, std::greater<int>());
for (int i = 1; i <= n; ++i) qw[1].push(a[i]);
for (int i = 0; i < m; ++i) {
pii p = std::max({std::make_pair(qw[1].empty() ? mininf : qw[1].front(), 1),
std::make_pair(qw[2].empty() ? mininf : qw[2].front(), 2),
std::make_pair(qw[3].empty() ? mininf : qw[3].front(), 3)});
int x = p.first + q * i, j = p.second;
qw[j].pop();
int b = 1ll * x * u / v, c = x - b;
qw[2].push(b - q - q * i);
qw[3].push(c - q - q * i);
if (i % t == t - 1) printf("%d ", x);
}
puts("");
for (int i = 1; i <= n + m; ++i) {
pii p = std::max({std::make_pair(qw[1].empty() ? mininf : qw[1].front(), 1),
std::make_pair(qw[2].empty() ? mininf : qw[2].front(), 2),
std::make_pair(qw[3].empty() ? mininf : qw[3].front(), 3)});
int x = p.first, j = p.second;
qw[j].pop();
if (i % t == 0) printf("%d ", x + q * m);
}
puts("");
return 0;
}