[题解]luogu_P3084(单调队列dp
Posted superminivan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[题解]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 ",f[n+1]); }
以上是关于[题解]luogu_P3084(单调队列dp的主要内容,如果未能解决你的问题,请参考以下文章