Slope Trick
首先注意到
因此,同一段路的能量消耗只与传送带速度
由此,设
且能量转移需要满足条件:
终值条件为
因为只涉及线性函数和最值,考虑 Slope Trick。每次转移都相当于如下两步骤:
- 插入一段斜率为
-1/(1+s_i) 的斜率段,长度为\Delta L_i/(2+s_i)+\Delta L_i/s_i ,并将整体左移-\Delta L_i/s_i ; - 删掉
0 左侧的部分,即斜率最小的、长度为\Delta L_i/s_i 的那些斜率段。(因为E_i\ge0 )
最后结束时需要计算
然后,自右向左把斜率段弹出,累加每段的时间增加值即可。
那些没有传送带的部分自然视为
核心代码如下:
int main() {
int n, L;
std::cin >> n >> L;
std::vector<std::array<long double, 2>> walkways;
walkways.reserve(n << 1);
int x = 0;
for (int i = 0; i < n; ++i) {
int l, r;
long double s;
std::cin >> l >> r >> s;
if (l > x) {
walkways.push_back({ (long double)(l - x), 0.0l });
x = l;
}
walkways.push_back({ (long double)(r - l), s });
x = r;
}
if (x < L) {
walkways.push_back({ (long double)(L - x), 0.0l });
x = L;
}
std::priority_queue<std::array<long double, 2>> slopes;
double res = 0.0;
for (int i = walkways.size() - 1; i >= 0; --i) {
auto [len, s] = walkways[i];
if (s < 1e-10l) {
slopes.push({ 1.0l, len / 2 });
res += len / 2;
} else {
slopes.push({ 1 / (1 + s), len / (2 + s) + len / s });
long double excess = len / s;
while (excess > 1e-10) {
auto [k, d] = slopes.top();
slopes.pop();
if (excess - d >= 0) {
excess -= d;
} else {
slopes.push({ k, d - excess });
excess = 0.0;
}
}
res += len / (2 + s);
}
}
while (!slopes.empty()) {
auto [k, d] = slopes.top();
slopes.pop();
res += k * d;
}
std::cout << std::fixed << std::setprecision(12) << res;
return 0;
}
时间复杂度为