思路甚妙的$DP$(蒟蒻并不会差分约束$QwQ$)
设$f(i)$为只考虑前$i$个,且第$i$头牛为斑点牛的最大值。
初始状态显然为$f(0)=0$。
转移:
- 每个区间只有一头斑点牛,第$i$头为斑点牛,则所有覆盖$i$的区间都不能转移,记$rr[i]$为覆盖点$i$的所有区间中最靠左的左端点。
- 每个区间必须有一头斑点牛,所有不覆盖$i$的区间必须有斑点牛,记$ll[i]$为不覆盖点$i$的区间中最靠右的左端点。
就有转移方程$f(i)=\max \{f(j)+1\}(ll[i]\le j \le rr[i])$
记所有区间中最靠右的左端点为$ml$,答案即为$\max \{f(i)\}(ml\le i \le n)$。(最靠右的区间必须有斑点牛)
可以用单调队列优化,时间复杂度$O(n)$。
对于预处理$ll[i],rr[i]$同样可以$O(n)$实现。对于每个区间$[L,R]$,将$rr[L]$与$L$取$min$;将$ll[R+1]$与$L$取$max$,然后$ll$正着扫一遍取前缀最大值,$rr$倒着扫一遍取后缀最小值。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define maxn 200005
#define inf 0x3f3f3f3f
using namespace std;
inline int read(){
int x=0,y=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return y?-x:x;
}
int ll[maxn],rr[maxn],f[maxn],line[maxn],head,tail;
inline void push(int p){
if(f[p]==-1)return;
while(head<=tail&&f[line[tail]]<=f[p])--tail;
line[++tail]=p;
}
int main(){
int n=read(),m=read(),l,r,ml=1,poi=-1,ans=-1;
for(register int i=1;i<=n;++i)
rr[i]=i;
for(register int i=1;i<=m;++i)
l=read(),r=read(),ll[r+1]=max(ll[r+1],l),rr[r]=min(rr[r],l),ml=max(ml,l);
for(register int i=1;i<=n;++i)
ll[i]=max(ll[i],ll[i-1]);
for(register int i=n-1;i;--i)
rr[i]=min(rr[i],rr[i+1]);
memset(f,-1,sizeof f);
f[0]=0,head=1;
for(register int i=1;i<=n;++i){
while(poi<rr[i]-1)++poi,push(poi);
while(head<=tail&&line[head]<ll[i])++head;
if(head<=tail)f[i]=f[line[head]]+1;
}
for(register int i=ml;i<=n;++i)
ans=max(ans,f[i]);
printf("%d\n",ans);
}