zoukankan      html  css  js  c++  java
  • [题解]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]);
    }
  • 相关阅读:
    第三方支付
    优化MySQL插入方法的五个妙招
    MySQL的数据类型和建库策略详解
    mysql 文本搜索
    mysql 存储过程
    mysql 游标的使用
    mysql 触发器
    mysql 保留点
    MySQL 使用硬链接配合truncate 删除2.2T的表
    25-ZigZag Conversion
  • 原文地址:https://www.cnblogs.com/superminivan/p/11604987.html
Copyright © 2011-2022 走看看