Bzoj:[Poi2011]Lightning Conductor:决策单调性优化DP详解
2016-03-25 08:00
483 查看
题目链接:2216:[Poi2011]Lightning Conductor
我们先把题目中的p单独放在等式的一边,发现
![](https://img-blog.csdn.net/20160325080037376?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
然后……然后查题解发现这个式子是决策单调性的式子QAQ
证明膜PoPoQQQ大爷:Orz Po姐
人生第一道决策单调性get!!!
对于决策单调性,我们可以用一个队列维护一段元素,队列中的每一个元素都有一个决策区间,代表这个区间中的数都可以用他来进行转移
没进行到一次转移,我们将队头元素的区间左端点+1,若左端点>右端点就出队
然后考虑当前决策点,如果他比队尾优,那么我们就可能可以将它加入队列,比较方法就是用最后一个元素a
比较算出的p的大小。因为我们在队列中维护的元素会覆盖整个[1,n]的元素,所以一旦当前元素比队尾优我们一定用不到队尾的元素了
然后进一步比较,由Po姐的证明我们可以知道光用a
比较是不正确的,于是我们比较队尾的左端点时的答案
一旦i比队尾的左端点的答案都优了,那么队尾就真废了,出队,知道有一个队尾的左端点不虚,那么我们在他的决策区间中查询到一个位置使得在这个位置上队尾又虚了,那么后面的所有转移就都由i这个点来完成了,入队
代码:
我们先把题目中的p单独放在等式的一边,发现
然后……然后查题解发现这个式子是决策单调性的式子QAQ
证明膜PoPoQQQ大爷:Orz Po姐
人生第一道决策单调性get!!!
对于决策单调性,我们可以用一个队列维护一段元素,队列中的每一个元素都有一个决策区间,代表这个区间中的数都可以用他来进行转移
没进行到一次转移,我们将队头元素的区间左端点+1,若左端点>右端点就出队
然后考虑当前决策点,如果他比队尾优,那么我们就可能可以将它加入队列,比较方法就是用最后一个元素a
比较算出的p的大小。因为我们在队列中维护的元素会覆盖整个[1,n]的元素,所以一旦当前元素比队尾优我们一定用不到队尾的元素了
然后进一步比较,由Po姐的证明我们可以知道光用a
比较是不正确的,于是我们比较队尾的左端点时的答案
一旦i比队尾的左端点的答案都优了,那么队尾就真废了,出队,知道有一个队尾的左端点不虚,那么我们在他的决策区间中查询到一个位置使得在这个位置上队尾又虚了,那么后面的所有转移就都由i这个点来完成了,入队
代码:
#include<cmath> #include<cstdio> #include<cstdlib> #include<iostream> #include<algorithm> using namespace std; const int maxn=1000000+10; int a[maxn],n; struct point{int l,r,p;}q[maxn]; double dp1[maxn],dp2[maxn]; double cal(int x,int y){ return a[x]+sqrt(abs(x-y))-a[y]; } int ask(point q,int x){ int l=q.l,r=q.r; while (l<=r){ int mid=(l+r)>>1; if (cal(q.p,mid)>cal(x,mid)) l=mid+1; else r=mid-1; }return l; } void get_dp(double *dp){ int h=1,t=0; for (int i=1;i<=n;++i){ q[h].l++; if (h<=t&&q[h].l>q[h].r) h++; if (h>t||(cal(i,n)>cal(q[t].p,n))){ while (h<=t&&cal(i,q[t].l)>=cal(q[t].p,q[t].l)) t--; if (h>t) q[++t]=(point){i,n,i}; else{ int x=ask(q[t],i); q[t].r=x-1; q[++t]=(point){x,n,i}; } }dp[i]=cal(q[h].p,i); } } int main(){ scanf("%d",&n); for (int i=1;i<=n;++i) scanf("%d",&a[i]); get_dp(dp1); for (int i=1;i<=n/2;++i) swap(a[i],a[n-i+1]); get_dp(dp2); for (int i=1;i<=n;++i) printf("%d\n",(int)(ceil)(max(dp1[i],dp2[n-i+1]))); }
相关文章推荐
- 端口详解(3)-源端口
- 华为交换机的后缀详解
- DLL(Dynamic Linkable Library) 详解说明
- rudy 重载方法 详解
- 留言板翻页的实现详解
- 前端轻量级MVC框架CanJS详解
- JS event使用方法详解
- 无边框窗口代码详解
- WHOIS类的修改版
- JAVASCRIPT THIS详解 面向对象
- base href 使用方法详解
- Redis教程(十):持久化详解
- Linux rpm 命令参数使用详解
- javascript快速排序算法详解
- 详解Android应用中屏幕尺寸的获取及dp和px值的转换
- 基于Android中dp和px之间进行转换的实现代码
- Android中dip、dp、sp、pt和px的区别详解
- 谈谈java的concurrent用法
- Java反射机制及Method.invoke详解
- HttpClient 在Java项目中的使用详解