#单调队列#洛谷 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);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值