[题解]luogu_P3084(单调队列dp

https://www.cnblogs.com/bztMinamoto/p/9375444.html

这题还可以dp做(我肯定想不出来

设$f[i]$为强制放在$i$的最大方案数,根据限制所有包含$i$的区间都不能再有点,最大只能从这些区间最靠左的左端点-1转移,最小不能跨过某个整区间转移,这样这个区间里就没有点,这样决策区间的左右端点$l[i],r[i]$就可以知道了

可是$l[i],r[i]$该怎么计算呢?发现这些左右端点是和区间端点密切相关的,再加思索发现类似于决策单调性一样的效果,区间内的决策点大概都是一样的,一开始我们先在各个区间的端点更新端点的取值,然后在正反扫一遍更新一遍所有点的lr取值

只能这么强行理解了,可能需要积累才能想到这些东西吧

这样$f[i]=max(f[j])+1(l[i]<=j<=r[i])$,

感觉l,r应该是单调的,实际上更新的时候$r[i]=min(r[i],r[i+1]),l[i]=max(l[i],l[i-1])$,能看出单调性是对的

然后类似于NOIP普及2017T4单调队列即可

代码细节较多也是抄的

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+9;
int n,m,l[maxn],r[maxn];//包含i的区间决策左端点/右端点 
int hd,tl,q[maxn],f[maxn];
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);//右端点为包含i的区间中最靠左的左端点-1,下面会更新一遍 
        l[y+1]=max(l[y+1],x);
    }
    for(int i=n;i>=1;i--)r[i]=min(r[i],r[i+1]);//在i-1之前的左端点必定在i前 
    for(int i=2;i<=n+1;i++)l[i]=max(l[i],l[i-1]); 
    int j=1;hd=tl=1;
    for(int i=1;i<=n+1;i++){//更新到n+1是方便判无解!! 
        while(j<=r[i]&&j<=n){
            if(f[j]==-1){++j;continue;}
            while(hd<=tl&&f[j]>f[q[tl]])tl--;
            q[++tl]=j;
            j++;
        }//不断加入决策点 
        while(hd<=tl&&q[hd]<l[i])++hd;
        if(hd<=tl)f[i]=f[q[hd]]+(i!=n+1?1:0);
        else f[i]=-1;
    }
    printf("%d\n",f[n+1]);
}

 

posted @ 2019-09-28 21:31  羊肉汤泡煎饼  阅读(174)  评论(0编辑  收藏  举报