单调队列优化dp

一本通题解

T2:

再也不想碰这道题了。。。写了一下午。

我们先设状态 dp[i][j] 表示前i个刷匠,考虑了前 j 个木板后所获得的最大价值(j 个木板可以有空余)。

然后枚举前 i 个刷匠,枚举每一条木板。

对于一条木板可以此刷匠根本不刷,或不刷当前木板,状转方程:

dp[i][j]=max(dp[i1][j],dp[i][j1])

还可以刷以这一块木板为结尾的连续一段区间,这个转移条件为 j>=s[i]jl[i]+1<=s[i],我们把这一段要刷的区间设为 [k+1,j] 状转方程

dp[i][j]=maxk=jl[i]k<s[i]{dp[i1][k]+p[i](jk)}

p[i]j 是固定的我们可以把它提出来

dp[i][j]=p[i]j+maxk=jl[i]k<s[i]{dp[i1][k]p[i]k}

后面这一堆我们可以用单调队列维护,注意到因为单调队列右端点是固定的 s[i],所以我们可以先预处理出单调队列然后维护单调队列的头即可。

注意一些边界条件特判。

T3:

比较巧妙的优化,因为注意到我们是从i的前k个dp数组中转化dp[i]的,所以单调队列来维护前k位dp最小值,但是,你会发现如果前面的高度小于此树高,就会带来1的贡献,但是它只有1啊,所以我们只需要让高度小的在单调队列前即可

注意:树高是严格小于

T4:

时隔许久,我终于来填单调队列这一道大坑了

现在看来也没有那么难

by the way,这道题题目描述错了,输入时b,t反了,原题为CF372C

首先我们枚举烟花,从时间从小到大的顺序,然后再枚举在哪个位置转移的话,就是从 ti ti1 这段时间能到这个位置的所有位置都可以转移到这,然后这个转移可以用单调队列优化

T5:

首先注意题目中的几个小细节:要先从0号节点出发,也就是我们需要手动添加一个距离为0,权值为0的节点,其次是可以在任意位置结束,就是只要存在dp值大于k就可以了

很明显这题一眼二分,然后我们根据二分出的g来确定单调队列的范围,然后转移即可

注:细节若一个点无法从原先的点转移过来,就把这个点的dp设为-inf

代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+5,inf=1e18;
int n,d,k,mx,q1,q2;
int u[N],w[N],q[N],dp[N];
void add(int x,int z){
    while(q1<=q2&&u[q[q1]]<z)  q1++;
    while(q1<=q2&&dp[q[q2]]<=dp[x])  q2--;
    if(u[x]<z)  return;
    q[++q2]=x;
}
bool check(int g){
    for(int i=0;i<=n;i++)  dp[i]=0,q[i]=0;
    dp[0]=0;
    u[0]=0;
    q1=1,q2=0;
    int l=max(d-g,1ll),r=d+g;
    for(int i=1,j=0;i<=n;i++){
        while(u[j]<=u[i]-l){
            add(j,u[i]-r);
            j++;
        }
        while(q1<=q2&&u[q[q1]]<u[i]-r)  q1++;
        if(q1>q2){
            dp[i]=-inf;
            continue;
        }
        dp[i]=dp[q[q1]]+w[i];
        if(dp[i]>=k)  return 1;
    }
    return 0;
}
signed main(){
    scanf("%lld%lld%lld",&n,&d,&k);
    for(int i=1;i<=n;i++){
        scanf("%lld%lld",&u[i],&w[i]);
        mx=max(u[i],mx);
    }
    int l=0,r=mx;
    while(l<r){
        int mid=(l+r)>>1;
        if(check(mid))  r=mid;
        else  l=mid+1;
    }
    printf("%lld",l);
}

T6:

本质上是单调栈,我们维护一个递增的单调栈,然后假如i元素被j元素弹出,说明i元素的一个边界为j元素,所以正反跑一遍单调栈,统计左右边界

T7:

板子题,然后记录一下从哪里转移过来的就好了

T8:

注:这里默认有一个限制,位置0与位置n+1被选择

二分单调队列长度,板子

T9:

我们设 dp[i](1,i) 不包含违禁词的最小减少量,然后我们设 f[i] 为以 i 结尾,不包含违禁词的区间的开头,注意:只有满足有单词以 i 结尾,才进行转移,我们枚举最后空格的位置 k,所以转移就是 dp[i]=maxk=f[i]1i{dp[k1]+a[k]}

然后我们对每一个单词跑一遍kmp,然后预处理出 f[i] ,只有有值才进行转移,转移范围就是 f[i],用单调队列维护即可

T10:

毒瘤题,题解不可看

朴素dp: fi=min{fj+maxk=j+1iak}j 满足 j=xiax<m

我们先预处理对于每一个 ij 的范围为 fi,然后对于这个范围我们可以维护一个单调递减的队列,这样就去掉了内层的 max 限制,考虑如何拆掉第二层限制,我们观察 fi 的性质,发现它是单调递增的,我们考虑我们的

代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define pii pair<int,int>
using namespace std;
const int N=1e5+5;
int n,m,q1,q2;
int a[N],sum[N],f[N],q[N],num[N],dp[N];
priority_queue<pii,vector<pii>,greater<pii> >mn,del;
signed main(){
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        if(a[i]>m){
            printf("-1");
            return 0;
        }
    }
    f[0]=1;
    for(int i=1;i<=n;i++){
        sum[i]=sum[i-1]+a[i];
        f[i]=f[i-1];
        while(sum[i]>m){
            sum[i]-=a[f[i]];
            f[i]++;
        }
        // printf("%d ",f[i]);
    }
    // printf("\n");
    q1=1,q2=0;
    for(int i=1;i<=n;i++){
        while(q1<=q2&&q[q1]<f[i]){
            del.push({num[q[q1]],q[q1]});
            q1++;
        }
        while(q1<=q2&&a[q[q2]]<=a[i]){
            del.push({num[q[q2]],q[q2]});
            q2--;
        }
        q[++q2]=i;
        if(q1==q2)  num[i]=dp[f[i]-1]+a[i];
        else  num[i]=dp[q[q2-1]]+a[i];
        mn.push({num[i],i});
        del.push({num[q[q1]],q[q1]});
        num[q[q1]]=dp[f[i]-1]+a[q[q1]];
        mn.push({num[q[q1]],q[q1]});
        while(!del.empty()){
            if(del.top()!=mn.top())  break;
            del.pop(),mn.pop();
        }
        if(i!=1)  dp[i]=mn.top().first;
        else  dp[i]=a[i];
        // printf("%d %d %d\n",dp[i],num[i],mn.top().second);
        // for(int j=q1;j<=q2;j++){
        //     printf("%d ",q[j]);
        // }
        // printf("\n");
    }
    printf("%lld",dp[n]);
    return 0;
}
posted @   daydreamer_zcxnb  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
点击右上角即可分享
微信分享提示