P10806 [CEOI 2024] 洒水器
Fracture_Dream · · 题解
前言
怎么题解区全是贪心的做法?来贡献一篇
"膜拜"同机房巨佬 @TTpandas 试图通过复杂度和正确性都错误的奇技淫巧通过此题。
题目大意
一个数轴上有
一个洒水器可以浇灌位于
思路
首先一眼二分答案,考虑如何 check。
如果你做过 CF1476F,那么这道题就很简单了,因为你会发现这就是个经典的前缀覆盖问题。
令
令
令
令
令
显然这些值是很好求的,重点在于
有如下三种转移:
-
- 若
dp_{i-1} \ge lasR_i ,当前洒水器右旋,那么dp_i \leftarrow \max\{R_i,LasL_i\} 。 - 找到最小的
l 满足dp_l \ge L_i ,当前洒水器左旋,显然将[l+1,i-1] 中的洒水器右旋。那么
最后判断一下
注意
代码
时间复杂度
这里只给出 check 的核心代码。
bool check(int mid) {
for (int i = 1 ; i <= n ; i ++) {
int k = lower_bound(f + 1 , f + m + 1 , s[i] - mid) - f - 1;
if (f[k] < s[i] - mid) L[i] = k;
else L[i] = 0;
}
for (int i = 1 ; i <= n ; i ++) {
int k = upper_bound(f + 1 , f + m + 1 , s[i] + mid) - f - 1;
if (f[k] >= s[i] && f[k] <= s[i] + mid) R[i] = k;
else R[i] = 0;
}
for (int i = 1 ; i <= n ; i ++) {
int k = upper_bound(f + 1 , f + m + 1 , s[i]) - f - 1;
if (f[k] >= s[i] - mid && f[k] <= s[i]) lasL[i] = k;
else lasL[i] = 0;
lasR[i] = lower_bound(f + 1 , f + m + 1 , s[i]) - f - 1;
}
for (int i = 1 ; i <= n ; i ++) Max[i][0] = R[i];
for (int j = 1 ; j <= 17 ; j ++) {
for (int i = 1 ; i + (1 << j) - 1 <= n ; i ++) {
Max[i][j] = max(Max[i][j - 1] , Max[i + (1 << j - 1)][j - 1]);
}
}
for (int i = 1 ; i <= n ; i ++) {
dp[i] = dp[i - 1] , pre[i] = i - 1 , dir[i] = -1;
if (dp[i - 1] >= lasR[i]) {
if (max(R[i] , lasL[i]) > dp[i]) {
dp[i] = max(R[i] , lasL[i]) , dir[i] = 1 , pre[i] = i - 1;
}
}
int l = 0 , r = i - 1;
while(l < r) {
int mid = l + r >> 1;
if (dp[mid] >= L[i]) r = mid;
else l = mid + 1;
}
if (dp[l] >= L[i]) {
if (max(GetMax(l + 1 , i - 1) , lasL[i]) > dp[i]) {
dp[i] = max(GetMax(l + 1 , i - 1) , lasL[i]);
pre[i] = l , dir[i] = -1;
}
}
}
return (dp[n] >= m);
}