Bzoj:[Poi2011]Lightning Conductor:决策单调性优化DP详解

题目链接:2216:[Poi2011]Lightning Conductor

我们先把题目中的p单独放在等式的一边,发现

然后……然后查题解发现这个式子是决策单调性的式子QAQ

证明膜PoPoQQQ大爷:Orz Po姐

人生第一道决策单调性get!!!

对于决策单调性,我们可以用一个队列维护一段元素,队列中的每一个元素都有一个决策区间,代表这个区间中的数都可以用他来进行转移

没进行到一次转移,我们将队头元素的区间左端点+1,若左端点>右端点就出队

然后考虑当前决策点,如果他比队尾优,那么我们就可能可以将它加入队列,比较方法就是用最后一个元素a[n]比较算出的p的大小。因为我们在队列中维护的元素会覆盖整个[1,n]的元素,所以一旦当前元素比队尾优我们一定用不到队尾的元素了

然后进一步比较,由Po姐的证明我们可以知道光用a[n]比较是不正确的,于是我们比较队尾的左端点时的答案

一旦i比队尾的左端点的答案都优了,那么队尾就真废了,出队,知道有一个队尾的左端点不虚,那么我们在他的决策区间中查询到一个位置使得在这个位置上队尾又虚了,那么后面的所有转移就都由i这个点来完成了,入队

代码:

#include
#include
#include
#include
#include
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])));
}




你可能感兴趣的:(OI,动态规划,决策单调性)