P13010题解

传送门:https://www.luogu.com.cn/problem/P13010

题目有点绕,做一个简单的转化,在第 \(i\) 天不使用某条动态车道并转向则在第 \(i+C\) 天后可使用这条动态车道。这说明第 \(j\) 天的车道需要在第 \(j-C\) 天及以前进行转向。这里有个显然的贪心:第 \(j\) 天所需要的车道在最后期限即第 \(j-c\) 天开始转向最优,如果提前转向会失去一条固定在某个方向的车道。

直接求出最大在如此大的数据无法做到(可以动态规划)。考虑二分答案转为判定问题。二分的最大负载值 \(mid\) 所对应的最少车道数 \(=\lceil \frac{c[i][p]}{mid} \rceil -1\)

根据以上的贪心结论。我们发现第 \(i\) 天的分配方案由第 \(i\) 到第 \(i+C\) 之间车辆的最大值所确定。我们只需要在其中进行决策即可。进一步地,我们在动态变化中决定最优分配方案,接下来为 \(i+1\) 天分配车道。

我们称在当前可以朝往任何方向的车道为自由车道。

\(c[i+C+1]>max([i,i+C])\)\(i+C+1\) 时刻加入决策范围导致车道不足需要从其他自由车道抽调。

\(c[i]>max([i+1,i+C+1])\) ,第 \(i\) 时刻退出决策范围,我们可以在 \(i+1\) 时刻转向第 \(i\) 时刻退出决策导致空出的车道提前给 \(i+C+1\) 以后的时刻做准备,又因为 \(i+C+1-i>C\) ,此时多余的车道在新的决策加入时已经转向完成,我们可以将其看作为自由车道。

接下来进行具体实现。

我们用数组记录当前分配的车道数和自由车道数。我们记 \([i,i+C]\) 这一窗口最值为 \(used\) ,记 \([i+1,i+C+1]\) 窗口最值为 \(new\) ,自由车道数为 \(free\) 。如果 \(used > new\) ,盈余的车道会成为自由车道,反之我们需要从自由车道抽出车道分配给不足的部分。我们让 \(free+=used-new\) ,在两类车道都完全计算后判断 \(free\) 是否为负,即可判断方案是否合法。这个过程利用单调队列可以很容易地维护。(当然 \(ST\) 也可以,但时间复杂度多个 \(log\) ,但也不至于 \(TLE\) ,但是整个决策过程本身就是一个单调队列的过程)

#include <bits/stdc++.h>


#define eps 1e-10

using namespace std;

int n, m, C;

const int N = 5e5 + 1;

int a[N][2], q[N][2];

bool check(double mid) {
    int free = n;
    int j = 0;
    int l[2] = {1, 1}, r[2] = {0, 0}, used[2] = {0, 0};
    for (int i = 1; i <= m; ++i) {
        while (j < i + C && j != m) {
            ++j;
            for (int p = 0; p <= 1; ++p) {
                if (l[p] <= r[p] && j - q[l[p]][p] == C + 1) ++l[p];
                while (l[p] <= r[p] && a[q[r[p]][p]][p] <= a[j][p]) --r[p];
                q[++r[p]][p] = j;
                int tmp = ceil(a[q[l[p]][p]][p] / mid) - 1;
                free += used[p] - tmp;
                used[p] = tmp;
            }
            if (free < 0) return false;
        }
    }
    return true;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int T;
    cin >> T;
    while (T--) {
        cin >> n >> m >> C;
        for (int i = 1; i <= m; ++i) cin >> a[i][0];
        for (int i = 1; i <= m; ++i) cin >> a[i][1];
        double l = 0, r = 5e5;
        while (r - l > eps) {
            double mid = (l + r) / 2;
            if (check(mid)) r = mid;
            else l = mid;
        }
        cout << fixed << setprecision(9) << r << '\n';
    }
    return 0;
}
posted @ 2025-07-14 20:48  Jefferyzzzz  阅读(7)  评论(0)    收藏  举报