title: 专题3 - 单调栈、单调队列
tags: [单调队列]
categories: [OI, 算法专题]

单调栈其实是单调队列的一种特殊情况,所以我们只看单调队列。
单调队列包含两端:过期端和淘汰端。
队列内从过期端到淘汰端,权值越来越小,进队时间越来越短。
本质上是在队列中存可能在现在/将来称为答案的节点,权值(价值)最高的点在过期端头,入队时间长且价值低的点将被剔除。
元素可以不为入队时间,也就是说可以针对任意两个元素的信息处理。
直接看代码:

#include <bits/stdc++.h>
using namespace std;
//#define int long long
#define REP(i,a,b) for(int i=(a);i<=(b);i++)
int n,k;
int a[1000100];
deque <int> q;
signed main(){
	cin >> n >>k;
	REP(i,1,n) cin >> a[i];
	REP(i,1,n){
		while (!q.empty() && a[q.back()] > a[i]) q.pop_back();
		q.push_back(i);
		if (i >= k){
			while (!q.empty() && q.front() <= i-k) q.pop_front();
			printf("%d ",a[q.front()]);
		}
	}
	printf("\n");
	while (!q.empty()) q.pop_front();
	REP(i,1,n){
		while (!q.empty() && a[q.back()] < a[i]) q.pop_back();
		q.push_back(i);
		if (i >= k){
			while (!q.empty() && q.front() <= i-k) q.pop_front();
			printf("%d ",a[q.front()]);
		}
	} 
	return 0;
}

习题

P2698 USACO12MAR Flowerpot S

原题 · 标签:#单调队列 #二分

考点

单调队列经典建模与应用

题意

有 \(N\) 个水滴,坐标为 \((x,y)\),每个水滴每秒向下移动1单位,落到x轴的时间等于自身纵坐标 \(y\)。
将花盆放在x轴上(一段连续区间),水滴落点 \(x\) 在区间内就会被接住。
要求:被接住的水滴中,最大下落时间 - 最小下落时间 ≥ D,求满足条件的花盆最小宽度;无法满足则输出 \(-1\)。

数据范围

  • \(1 \le N \le 10^5\)
  • \(1 \le D \le 10^6\)
  • \(0 \le x,y \le 10^6\)

思路

首先很好想到二分,两个元素,宽度看做过期指标(y轴),时间看做权值(x轴),就很显然了。
两个元素,有过期信息和最值信息的处理,单调队列!
使用单调队列维护宽度窗口内的最大值和最小值,check是否大于等于D,在外面二分宽度。

代码

// 1
#include <bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define f(i,a,b) for(int i=(a);i<=(b);i++)
#define ll long long
#define pii pair<int,int>
#define DEBUG 0
#if DEBUG
#define d(x) cout << #x << " = " << x << endl;
#define da(a,l,r) cout << #a << "[" << l << "-" << r << "]:"; f(i,l,r) cout << a[i] << " "; cout << endl;
#else
#define d(x)
#define da(a,l,r) 
#endif

struct ddzSt{
	int x,y;
	bool operator < (ddzSt st2){
		return y < st2.y;
	}
};

int n,nd;
vector<ddzSt> s;
struct DdzMaxQ{
	vector<int> q;
	int head,tail;
	void init(int size){
		q.resize(size+2);
		head=1;tail=0;
	}
	void push(int idx,const vector<ddzSt>& nums){
		while(head<=tail && nums[q[tail]].y <= nums[idx].y) tail--;
		q[++tail]=idx;
	}
	void popUntil(int left){
		while(head<=tail && q[head]<left) head++;
	}
	int getY(const vector<ddzSt>& nums){
		return nums[q[head]].y;
	}
};
struct DdzMinQ{
	vector<int> q;
	int head,tail;
	void init(int size){
		q.resize(size+2);
		head=1;tail=0;
	}
	void push(int idx,const vector<ddzSt>& nums){
		while(head<=tail && nums[q[tail]].y >= nums[idx].y) tail--;
		q[++tail]=idx;
	}
	void popUntil(int left){
		while(head<=tail && q[head]<left) head++;
	}
	int getY(const vector<ddzSt>& nums){
		return nums[q[head]].y;
	}
};

DdzMaxQ qMax;
DdzMinQ qMin;

bool check(int mid){
	qMax.init(n);
	qMin.init(n);
	int left=1;
	f(i,1,n){
		qMax.push(i,s);
		qMin.push(i,s);
		while(s[i].x - s[left].x > mid) left++;
		qMax.popUntil(left);
		qMin.popUntil(left);
		if(qMax.getY(s) - qMin.getY(s) >= nd) return 1;
	}
	return 0;
}

bool cmp(ddzSt a,ddzSt b){
	return a.x < b.x;
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n >> nd;
	s.resize(n+1);
	f(i,1,n){
		cin >> s[i].x >> s[i].y;
	}
	sort(s.begin()+1,s.end(),cmp);
	int lef=0,rig=1e6,ans=-1;
	while(lef<=rig){
		int mid=(lef+rig)/2;
		if(check(mid)){
			ans=mid;
			rig=mid-1;
		}
		else lef=mid+1;
	}
	cout << ans;
	return 0;
}