「USACO13OPEN」Photo「单调队列」

版权声明:本文为hzy原创文章,未经博主允许不可随意转载。 https://blog.csdn.net/Binary_Heap/article/details/82822497

题目传送门

题解

看起来是差分约束,但是USACO出题人又卡SPFA了!

这里有一种巧妙的DP方法。

f [ i ] f[i] : i i 必须放,最多能放多少个.

考虑上一个可以放在哪里。
l [ i ] l[i] : 完全在 i i 左边的区间,左端点的最大值 (不能不放)
r [ i ] r[i] : 包含 i i 的区间中,左端点的最小值 1 -1 (只能放 1 1 个)
这个怎么求呢?
给一个区间 [ x , y ] [x,y] ,用 x 1 x-1 去更新 r [ y ] r[y] ,用 x x 去更新 l [ y + 1 ] l[y+1] 。最后 l l 取前缀最大值, r r 取后缀最小值。
(这个具体原因可以根据 l , r l,r 的定义思考一下)

于是:
f [ i ] = m a x ( f [ j ] ) + 1 , l [ i ] j r [ i ] f[i]=max(f[j])+1,l[i]\leq j \leq r[i]
考虑到 l , r l,r 数组满足单调性,可以使用单调队列优化(线段树优化应该也行)

#include <cstdio>
#include <ios>

using std :: max;
using std :: min;

const int N = 200010;

int n, m, ans;
int l[N], r[N], f[N];
//l(i):完全在i左边的区间,左端点的最大值 (不能不放)
//r(i):包含i的区间中,左端点的最小值-1 (只能放1个) 
int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n + 1; i ++) r[i] = i - 1; //一个位置只能有一个点 
	for(int i = 1, x, y; i <= m; i ++) {
		scanf("%d%d", &x, &y);
		r[y] = min(r[y], x - 1);
		l[y + 1] = max(l[y + 1], x);
	}
	for(int i = n; i; i --) r[i] = min(r[i], r[i + 1]);
	for(int i = 1; i <= n; i ++) l[i] = max(l[i], l[i - 1]);
	static int hd = 0, bk = 1, q[N] = {0}, j = 1;
	for(int i = 1; i <= n + 1; i ++) {
		for(; j <= n && j <= r[i]; ++ j)
			if(~ f[j]) {
				for(; hd < bk && f[j] > f[q[bk - 1]]; -- bk) ;
				q[bk ++] = j;
			}
		for(; hd < bk && q[hd] < l[i]; ++ hd) ;
		f[i] = hd < bk ? f[q[hd]] + 1 : -1;
		if(~ f[i]) ans = max(ans, f[i]);
	}
	printf("%d\n", (~ f[n + 1]) ? f[n + 1] - 1 : -1);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Binary_Heap/article/details/82822497
今日推荐