题目大意
F a r m e r J o h n Farmer John FarmerJohn决定为他的 N N N头排列好的奶牛 ( 1 < = N < = 200 , 000 ) (1 <= N<= 200,000) (1<=N<=200,000)做一张全景合照。这 N N N头奶牛分别以 1.. N 1..N 1..N进行编号。他一共拍了 M ( 1 < = M < = 100 , 000 ) M(1<= M <=100,000) M(1<=M<=100,000)张相片,每张相片都只包含有一部分位置连续的奶牛:第i张照片涵盖着编号从 a i a_i ai到 b i b_i bi的所有奶牛。当然,这些照片合起来并不保证包含所有的牛。
F a r m e r J o h n Farmer John FarmerJohn拍摄完所有的相片后,注意到一个很有趣的现象:他拍的每张照片中有且仅有一只奶牛身上有斑点。 F a r m e r J o h n Farmer John FarmerJohn知道他的奶牛中有一部分是身上有斑点的,但他从来没有数过这种奶牛的数目。请根据 F a r m e r J o h n Farmer John FarmerJohn的这些照片,确定可能出现的斑点牛的最大的数目;若从 F a r m e r J o h n Farmer John FarmerJohn的照片中无法推测斑点牛的数目,则输出 − 1 -1 −1。
题目解析
差分约束,可以过的
这里讲
D
P
DP
DP,单调队列优化。
首先,设
F
[
i
]
F[i]
F[i]表示
i
i
i必须放斑点牛,前
i
i
i个牛中最多能放多少个斑点牛.
我们可以考虑出上一个斑点牛放在哪里,
l
[
i
]
l[i]
l[i]表示放了斑点牛在
i
i
i后,上一个斑点牛最远可以放在哪里。即
i
i
i所在区间的上一个区间的最左端。
r
[
i
]
r[i]
r[i]表示放了斑点牛在
i
i
i后,上一个斑点牛最近可以放在哪里。即
i
i
i所在区间的上一个区间的最右端。
给个区间
[
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取后缀最小值。
于是
F
[
i
]
=
M
a
x
(
F
[
j
]
)
+
1
F[i]=Max(F[j])+1
F[i]=Max(F[j])+1
(
l
[
i
]
<
=
j
<
=
r
[
i
]
)
(l[i]<=j<=r[i])
(l[i]<=j<=r[i])
考虑到
l
,
r
l,r
l,r数组满足单调性,可以使用单调队列优化
#include<bits/stdc++.h>
using namespace std;
const int N=201000;
int n,m;
int l[N],r[N],f[N];
deque<int> q;
int main()
{
scanf("%d%d",&n,&m);
int x,y;
for(int i=1;i<=n+1;i++) r[i]=i-1;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
l[y+1]=max(l[y+1],x);
r[y]=min(r[y],x-1);
}
memset(f,0,sizeof(f));
for(int i=2;i<=n+1;i++)
l[i]=max(l[i],l[i-1]);
for(int i=n;i>=1;i--)
r[i]=min(r[i+1],r[i]);
q.push_front(0);
int j=1;
for(int i=1;i<=n+1;i++)
{
while(j<=r[i]&&j<=n)
{
if(f[j]==-1)
{
j++;
continue;
}
while(!q.empty()&&f[j]>f[q.back()])
q.pop_back();
q.push_back(j);
j++;
}
while(q.front()<l[i]&&!q.empty())
q.pop_front();
if(!q.empty())
{
if(i!=n+1)
f[i]=f[q.front()]+1;
else
f[i]=f[q.front()];
}
else f[i]=-1;
}
printf("%d\n",f[n+1]);
}