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;
}

单调队列,二分,贪心
浙公网安备 33010602011771号