#单调队列#洛谷 2698 [USACO12MAR]花盆Flowerpot

题目

给出N滴水的坐标(X,Y),y表示水滴的高度,x表示它下落到x轴的位置。每滴水每秒从(x,y)到(x,y-1)。你需要把花盆放在x轴上的某个位置,使得从开始接水到水滴完之间的时间差至少为D,只要水滴落到x轴上,与花盆的边沿对齐,就认为被接住,求最小的花盆的宽度W。


分析

单调队列,维护一个滑动窗口,同时用两个单调队列维护滑动窗口的最大值和最小值,存位置
首先出队挺简单的

while (h1<=t1&&q1[h1]<l) h1++;
while (h2<=t2&&q2[h2]<l) h2++;

之后当队首的时间差小于d,当无法维护最大值和最小值时不断从队尾出队,并将新的水滴入队

while (a[q1[h1]].y-a[q2[h2]].y<d&&r<n){
    r++;
    while (a[q1[t1]].y<a[r].y&&h1<=t1) t1--; q1[++t1]=r;
    while (a[q2[t2]].y>a[r].y&&h2<=t2) t2--; q2[++t2]=r;
}

最后判断计算最小值不想多说


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
struct node{int x,y;}a[100001];
int n,d,ans=2147483647,q1[100001],q2[100001];
int in(){
    int ans=0; char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=ans*10+c-48,c=getchar();
    return ans;
}
bool cmp(node x,node y){return x.x<y.x;}
void rn(int &a,int &b){a=in(); b=in();}
int main(){
    rn(n,d); int r=0,h1=1,h2=1,t1=0,t2=0;
    for (int i=1;i<=n;i++) rn(a[i].x,a[i].y);
    sort(a+1,a+1+n,cmp);//按横坐标排序
    for (int l=1;l<=n;l++){
        while (h1<=t1&&q1[h1]<l) h1++;//最大值单调队列出队
        while (h2<=t2&&q2[h2]<l) h2++;//最小值单调队列出队
        while (a[q1[h1]].y-a[q2[h2]].y<d&&r<n){//时间差不满足
            r++;
            while (a[q1[t1]].y<a[r].y&&h1<=t1) t1--; q1[++t1]=r;//覆盖低的水滴
            while (a[q2[t2]].y>a[r].y&&h2<=t2) t2--; q2[++t2]=r;//覆盖高的水滴
        }
        if (a[q1[h1]].y-a[q2[h2]].y>=d) ans=min(ans,a[r].x-a[l].x);//当时间差满足时统计最小值
    } 
    if (ans==2147483647) return !puts("-1");//算不出最小值
    else return !printf("%d",ans);
}

猜你喜欢

转载自blog.csdn.net/sugar_free_mint/article/details/80946530