https://www.acwing.com/problem/content/135
题意
给定一些蚯蚓,每秒挑选一根最长的蚯蚓斩断。
斩断后,这一根长度为
x
x
x的蚯蚓将变成两根长度分别为
⌊
p
X
⌋
\lfloor pX \rfloor
⌊pX⌋和
X
−
⌊
p
X
⌋
X-\lfloor pX \rfloor
X−⌊pX⌋的蚯蚓,同时其他蚯蚓的长度将会增加一个非负整数
q
q
q。
求
m
m
m秒内每一根被切断的蚯蚓被切断前的长度,以及
m
m
m秒后所有蚯蚓的长度。
思路
由于每一轮未被切割的蚯蚓,其长度都要加上 p p p,所以不妨记录蚯蚓的原长度以及增加的偏移量。(即真实长度为原长度+偏移量),其中每一轮都有偏移量 Δ = Δ + p \Delta=\Delta+p Δ=Δ+p。
而对于被切断的蚯蚓,其长度并不增长。我们可以考虑将其长度减去 p p p,然后认为对所有蚯蚓长度增加 p p p即可。
因此,我们有如下流程
- 初始情况下, Δ = 0 \Delta=0 Δ=0,代表尚没有切割过一条蚯蚓,蚯蚓的长度也没有增长。
- 每一秒,取出原长为 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 Xreal−⌊pXreal⌋的两条。
-
Δ
=
Δ
+
p
\Delta=\Delta+p
Δ=Δ+p,代表其他蚯蚓的长度又增加了
p
p
p。
此处的顺序和算法竞赛进阶指南上略有区别,与yxc的代码一致。 - 此时, ⌊ 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 Xreal−⌊pXreal⌋代表的是两条蚯蚓的真实长度,但是我们需要记录的是和真实长度相差 Δ \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 Xreal−⌊pXreal⌋−Δ
基于这些结论,我们还可以进一步对算法进行改进。
接下来,我们要证明这样一个性质:
对于两条长度分别为
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+q⌋≥⌊p(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 X−⌊pX⌋一段,有
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 X1−⌊pX1⌋+q≥X2+q−⌊p(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;
}