题解
看起来是差分约束,但是USACO出题人又卡SPFA了!
这里有一种巧妙的DP方法。
f [ i ] f[i] f[i] : i i i必须放,最多能放多少个.
考虑上一个可以放在哪里。
l
[
i
]
l[i]
l[i] : 完全在
i
i
i左边的区间,左端点的最大值 (不能不放)
r
[
i
]
r[i]
r[i] : 包含
i
i
i的区间中,左端点的最小值
−
1
-1
−1 (只能放
1
1
1个)
这个怎么求呢?
给一个区间
[
x
,
y
]
[x,y]
[x,y],用
x
−
1
x-1
x−1去更新
r
[
y
]
r[y]
r[y],用
x
x
x去更新
l
[
y
+
1
]
l[y+1]
l[y+1]。最后
l
l
l取前缀最大值,
r
r
r取后缀最小值。
(这个具体原因可以根据
l
,
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]
f[i]=max(f[j])+1,l[i]≤j≤r[i]
考虑到
l
,
r
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;
}