【转载】【队列】【数学】[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) ) 的时间复杂度下解决本题。

算法步骤

  1. 循环 ( 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 ) 放回集合中。
  2. 通过上述方法解决问题,但该方法的效率不够优秀。

更优的做法(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 )。需要证明:

  1. ( \lfloor px_1 \rfloor + q \geq \lfloor p(x_2 + q) \rfloor )
  2. ( 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;
}

posted @ 2024-10-16 11:29  peterzh6  阅读(4)  评论(0编辑  收藏  举报