洛谷P3515 [POI2011]Lightning Conductor(动态规划,决策单调性,单调队列)

洛谷题目传送门

疯狂%%%几个月前就秒了此题的Tyher巨佬

借着这题总结一下决策单调性优化DP吧。蒟蒻觉得用数形结合的思想能够轻松地理解它。

首先,题目要我们求所有的pi,那么把式子变一下

piajai+|ij|

pi=maxj=1n{aj+|ij|}ai

绝对值看着很不爽,我们把它拆开

pi=max(maxj=1i{aj+ij},maxj=in{aj+ji})ai

单独看前一部分

pi=maxj=1i{aj+ij}ai

很明显是个要用决策单调性优化的式子。把序列翻转以后,后一部分的算法和前面是一样的,所以只讨论前一部分了。

对于每个j,把aj+ij看成关于i的函数fj。我们要做的就是在所有ji的函数中找到最值。比如样例:

观察发现,真正有用的函数只有最上面那个!然而实际情况比这个稍复杂些。sqrt的增速是递减的,因此可能存在一个j比较小的函数,在某一时刻被j比较大的函数反超。我们大概需要维护这样的若干个函数:

我们用队列实现决策二分栈(不懂的可以参考一下蒟蒻的blog),按j从小到大依次维护这些函数。显然,对于其中任意两个相邻的函数ft,ft+1,它们都有一个临界值kt,t+1。显然序列中的k1,2,k2,3...也要严格递增。否则,如果kt,t+1kt+1,t+2,可以想象ft+1根本没有用。

for一遍i,我们尝试着把fi加入队列。这时候为了保证k递增,设队尾决策为t,我们判断,如果kt1,tkt,i(此时会有ft(kt1,t)fi(kt1,t)),那么t没用,出队。

该出去的都出去后,i就可以加入队尾了。这时候可以来求pi了。我们检查一下队首决策h,如果th,h+1i,说明h的巅峰时刻已经过去,出队。最后队首就是所有函数中的最大值。

貌似并没有用到什么三元组啊qwq

update:感谢孤独·粲泽的指正,二分上下界确实该调调

不过还是没有用到什么三元组啊qwq,蒟蒻之前都把临界值k存下了,直接用就可以啦

#include<bits/stdc++.h>
#define RG register
#define R RG int
#define G if(++ip==iend)fread(ip=buf,1,N,stdin)
#define calc(i,j) a[j]+sq[i-j]//计算函数值
using namespace std;
const int N=5e5+9;
char buf[N],*iend=buf+N,*ip=iend-1;
int n,a[N],q[N],k[N];
double p[N],sq[N];
inline int in(){
    G;while(*ip<'-')G;
    R x=*ip&15;G;
    while(*ip>'-'){x*=10;x+=*ip&15;G;}
    return x;
}
inline void chkmx(RG double&x,RG double y){
    if(x<y)x=y;
}
inline int bound(R x,R y){//二分临界值k
    R l=y,r=k[x]?k[x]:n,m,ret=r+1;//控制二分上下界
    while(l<=r){
        m=(l+r)>>1;
        if(calc(m,x)<=calc(m,y))
            ret=m,r=m-1;
        else l=m+1;
    }
    return ret;
}
void work(){
    for(R h=1,t=0,i=1;i<=n;++i){
        while(h<t&&calc(k[t-1],q[t])<calc(k[t-1],i))--t;//维护k单调
        k[t]=bound(q[t],i);q[++t]=i;
        while(h<t&&k[h]<=i)++h;//将已经不优的决策出队
        chkmx(p[i],calc(i,q[h]));//因为做两遍所以取max
    }
}
int main(){
    n=in();
    R i,j;
    for(i=1;i<=n;++i)
        a[i]=in(),sq[i]=sqrt(i);
    work();
    for(i=1,j=n;i<j;++i,--j)//序列翻转
        swap(a[i],a[j]),swap(p[i],p[j]);
    work();
    for(R i=n;i;--i)//翻转过了所以要倒着输出
        printf("%d\n",max((int)ceil(p[i])-a[i],0));
    return 0;
}
posted @   Flash_Hu  阅读(2405)  评论(0编辑  收藏  举报
编辑推荐:
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示
哥伦布
-1°
10:09发布
哥伦布
10:09发布
-1°
西南风
2级
空气质量
相对湿度
87%
今天
小雨
-1°/13°
周六
多云
-3°/10°
周日
-4°/2°