P2698 [USACO12MAR] Flowerpot 题解


true
Flowerpot
USACO 2012 March Silver
提高+ /省选-
#3498db
  • Luogu P2698

题目要求我们在 $x$ 轴上找到最短的一个区间,使得该区间内所有点的纵坐标的极差不小于给定的一个数 $D$。

我们首先可以想到的做法就是二分,将其转化成为一个判定问题。
因为一旦找到了一个合法区间,我们如果继续扩展区间端点的话肯定还是能满足条件的,所以可以二分。

那我们考虑怎么判断。

我们可以枚举左端点,然后找到一个最近的右端点使得其满足条件,然后判断区间长度是否小于等于当前二分的长度即可。
我们还可以发现,区间右端点是单调不降的,这就给了我们使用滑动窗口的机会。

然后我们就可以好好想一想了。

我们每一次做滑动窗口统计到区间长度的时候,为什么不记录下来然后取最小值呢?毕竟这些答案都是固定的。

于是我们就光荣舍弃掉了二分,虽然复杂度没什么变化,因为复杂度瓶颈是排序。

具体做法就是首先将所有点按照横坐标排个序,然后拿两个单调队列维护区间最大值和区间最小值,不断移动左右端点即可。

参考代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 100010;
int n;
struct Raindrop
{
int x, y;
bool operator < (const Raindrop &a)const
{
return x < a.x;
}
}a[N];
int t[N], b[N];
int h1, t1, h2, t2;
int main()
{
int d;
scanf("%d%d", &n, &d);
for(int i = 1; i <= n; i++)
{
scanf("%d%d", &a[i].x, &a[i].y);
}
sort(a + 1, a + 1 + n);

int ans = 1e9;
h1 = h2 = 1;
for(int l = 1, r = 0; l <= n; l++)
{
while(h1 <= t1 && t[h1] < l)h1++;
while(h2 <= t2 && b[h2] < l)h2++;
while(a[t[h1]].y - a[b[h2]].y < d && r < n)
{
r++;
while(h1 <= t1 && a[t[t1]].y < a[r].y)t1--;
t[++t1] = r;
while(h2 <= t2 && a[b[t2]].y > a[r].y)t2--;
b[++t2] = r;
}
if(a[t[h1]].y - a[b[h2]].y >= d)
ans = min(ans, a[r].x - a[l].x);
}
if(ans >= 1e9)ans = -1;
printf("%d\n", ans);
return 0;
}