[BZOJ2276][Poi2011]Temperature(单调队列)

Address

洛谷P3522
BZOJ2276
LOJ#2164

Solution

先解决一个小问题:如何判断一个连续子段是否合法。
很容易得出, a [ x … y ] a[x\dots y] a[xy] 合法当且仅当对于每个 i ∈ [ x , y ] i\in[x,y] i[x,y] 都有 min ⁡ r [ i … y ] ≥ l i \min r[i\dots y]\ge l_i minr[iy]li
我们从左到右枚举 y y y ,需要维护出这时候满足条件的最小的 x x x
显然 x x x y y y 单调不减。初始时 x x x 1 1 1
这样我们就能用尺取法计算答案了。
用一个单调队列,维护 min ⁡ r [ i … y ] \min r[i\dots y] minr[iy] 相同的段。
也就是说单调队列中每个位置是一个区间 [ a , b ] [a,b] [a,b] ,满足 min ⁡ r [ a … y ] \min r[a\dots y] minr[ay] min ⁡ r [ a + 1 … y ] \min r[a+1\dots y] minr[a+1y] … \dots min ⁡ r [ b … y ] \min r[b\dots y] minr[by] 相同。同时每个位置需要维护 min ⁡ r [ a … y ] \min r[a\dots y] minr[ay] 以及 max ⁡ l [ a … b ] \max l[a\dots b] maxl[ab]
y + 1 y+1 y+1 插入队列时,
就将队尾所有满足 min ⁡ r [ a … y ] ≥ a y + 1 \min r[a\dots y]\ge a_{y+1} minr[ay]ay+1 的区间全部踢出队列,设踢出的最后一个区间是 [ u , v ] [u,v] [u,v]
然后把区间 [ u , y + 1 ] [u,y+1] [u,y+1] 插入队尾并维护 min ⁡ r [ u … y + 1 ] \min r[u\dots y+1] minr[uy+1] max ⁡ l [ u … y + 1 ] \max l[u\dots y+1] maxl[uy+1]
如果这时候出现 min ⁡ r [ u … y + 1 ] \min r[u\dots y+1] minr[uy+1] (即 a y + 1 a_{y+1} ay+1 &lt; max ⁡ l [ u … y + 1 ] &lt;\max l[u\dots y+1] <maxl[uy+1] 的情况,那么这种情况是不合法的。
这时候不停地从队首出队( x x x 加一),直到 a y + 1 ≥ max ⁡ l [ x … y + 1 ] a_{y+1}\ge\max l[x\dots y+1] ay+1maxl[xy+1] 为止。
我们还需要另一个单调队列维护 max ⁡ l [ x … y + 1 ] \max l[x\dots y+1] maxl[xy+1] 的值。
时间复杂度 O ( n ) O(n) O(n) 非常优秀。

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)

inline int read()
{
	int res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	return bo ? ~res + 1 : res;
}

template <class T>
T Max(T a, T b) {return a > b ? a : b;}

const int N = 1e6 + 5;

int n, l[N], r[N], H1, T1, Q2[N], H2, T2, ans,
L[N], R[N], maxl[N], maxr[N];

int main()
{
	int i;
	n = read();
	For (i, 1, n) l[i] = read(), r[i] = read();
	For (i, 1, n)
	{
		int lst = i, mxl = l[i];
		while (H1 < T1 && maxr[T1] >= r[i])
			lst = L[T1], mxl = Max(mxl, maxl[T1--]);
		L[++T1] = lst; R[T1] = i;
		maxl[T1] = mxl; maxr[T1] = r[i];
		while (H2 < T2 && l[Q2[T2]] < l[i]) T2--;
		Q2[++T2] = i;
		if (maxr[T1] < maxl[T1])
		{
			H1 = T1 - 1;
			while (H2 < T2 && maxr[T1] < l[Q2[H2 + 1]])
				lst = Q2[++H2];
			L[T1] = lst + 1;
			maxl[T1] = l[Q2[H2 + 1]];
		}
		ans = Max(ans, R[T1] - L[H1 + 1] + 1);
	}
	std::cout << ans << std::endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值