ACWING 133 蚯蚓

https://www.acwing.com/problem/content/135

题意

给定一些蚯蚓,每秒挑选一根最长的蚯蚓斩断。
斩断后,这一根长度为 x x x的蚯蚓将变成两根长度分别为 ⌊ p X ⌋ \lfloor pX \rfloor pX X − ⌊ p X ⌋ X-\lfloor pX \rfloor XpX的蚯蚓,同时其他蚯蚓的长度将会增加一个非负整数 q q q
m m m秒内每一根被切断的蚯蚓被切断前的长度,以及 m m m秒后所有蚯蚓的长度。

思路

由于每一轮未被切割的蚯蚓,其长度都要加上 p p p,所以不妨记录蚯蚓的原长度以及增加的偏移量。(即真实长度为原长度+偏移量),其中每一轮都有偏移量 Δ = Δ + p \Delta=\Delta+p Δ=Δ+p

而对于被切断的蚯蚓,其长度并不增长。我们可以考虑将其长度减去 p p p,然后认为对所有蚯蚓长度增加 p p p即可。

因此,我们有如下流程

  1. 初始情况下, Δ = 0 \Delta=0 Δ=0,代表尚没有切割过一条蚯蚓,蚯蚓的长度也没有增长。
  2. 每一秒,取出原长为 X o r i g i n a l X_{original} Xoriginal,真实长度为 X r e a l = X o r i g i n a l + Δ X_{real}=X_{original}+\Delta Xreal=Xoriginal+Δ的一条蚯蚓,将其切分为 ⌊ p X r e a l ⌋ \lfloor pX_{real} \rfloor pXreal X r e a l − ⌊ p X r e a l ⌋ X_{real}-\lfloor pX_{real} \rfloor XrealpXreal的两条。
  3. Δ = Δ + p \Delta=\Delta+p Δ=Δ+p,代表其他蚯蚓的长度又增加了 p p p
    此处的顺序和算法竞赛进阶指南上略有区别,与yxc的代码一致。
  4. 此时, ⌊ p X r e a l ⌋ \lfloor pX_{real} \rfloor pXreal X r e a l − ⌊ p X r e a l ⌋ X_{real}-\lfloor pX_{real} \rfloor XrealpXreal代表的是两条蚯蚓的真实长度,但是我们需要记录的是和真实长度相差 Δ \Delta Δ相对长度,所以我们将放回集合的两条蚯蚓的长度是 ⌊ p X r e a l ⌋ − Δ \lfloor pX_{real} \rfloor - \Delta pXrealΔ X r e a l − ⌊ p X r e a l ⌋ − Δ X_{real}-\lfloor pX_{real} \rfloor -\Delta XrealpXrealΔ

基于这些结论,我们还可以进一步对算法进行改进。

接下来,我们要证明这样一个性质:
对于两条长度分别为 X 1 X_1 X1 X 2 X_2 X2的蚯蚓,在 T T T时刻 X 1 X_1 X1被切割,而在 T T T时刻之后 X 2 X_2 X2被切割。则它们都被切割之后,必定有 X 1 X_1 X1切割产生的两条蚯蚓不短于 X 2 X_2 X2切割产生的两条蚯蚓。

以下较简略,详见蓝皮书和yxc的视频。

对于 ⌊ p X ⌋ \lfloor pX \rfloor pX一段,有

⌊ p X 1 ⌋ + q = ⌊ p X 1 + q ⌋ ≥ ⌊ p ( X 2 + q ) ⌋ \lfloor pX_1 \rfloor + q=\lfloor pX_1+q \rfloor \geq \lfloor p(X_2+q)\rfloor pX1+q=pX1+qp(X2+q)⌋
这证明了对于被切割成 p X pX pX的一段蚯蚓,若 X 1 X_1 X1先于 X 2 X_2 X2被切割,则有 X 1 X_1 X1切割产生的不短于 X 2 X_2 X2切割产生的。

同理对于 X − ⌊ p X ⌋ X-\lfloor pX \rfloor XpX一段,有

X 1 − ⌊ p X 1 ⌋ + q ≥ X 2 + q − ⌊ p ( X 2 + q ) ⌋ X_1-\lfloor pX_1 \rfloor +q \geq X_2 +q -\lfloor p(X_2+q) \rfloor X1pX1+qX2+qp(X2+q)⌋

因此,我们可以利用单调性维护最长的蚯蚓,也就是取单调队列的队首元素。

#include <iostream>
#include <queue>
#include <algorithm>
typedef long long ll;
using namespace std;
const int N = 100010;
const int M = 7000010;
int n;       // n只蚯蚓
int m;       // m秒时间
int q;       // 增加长度非负整数q
int u, v, t; // p=u/v,每t秒输出最长蚯蚓
int q1[N];
int q2[M], q3[M];
int delta;
int tt1;
int tt2 = -1, tt3 = -1;
int hh1, hh2, hh3;

int get_max()
{
    int x = -2e9;
    if (hh1 <= tt1)
        x = max(x, q1[hh1]);
    if (hh2 <= tt2)
        x = max(x, q2[hh2]);
    if (hh3 <= tt3)
        x = max(x, q3[hh3]);
    if (hh1 <= tt1 && x == q1[hh1])
        hh1++;
    else if (hh2 <= tt2 && x == q2[hh2])
        hh2++;
    else
        hh3++;
    return x;
}

int main()
{
    cin >> n >> m >> q >> u >> v >> t;
    for (int i = 0; i < n; i++)
        cin >> q1[i];
    sort(q1, q1 + n);
    reverse(q1, q1 + n);
    tt1 = n - 1;
    for (int i = 1; i <= m; i++)
    { // m秒,每秒一次切割
        int x = get_max();
        x += delta;
        if (i % t == 0)
            cout << x << ' ';       // 每t秒输出一次
        int left = x * 1ll * u / v; //(p=u/v)
        int right = x - left;
        delta += q;
        left -= delta;
        right -= delta;
        q2[++tt2] = left;
        q3[++tt3] = right;
    }
    cout << endl;
    for (int i = 1; i <= n + m; i++)
    {
        int x = get_max();
        if (i % t == 0)
            cout << x + delta << ' ';
    }
    cout << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值