[POI2011]Lightning Conductor

动态规划!

双倍经验(1)

双倍经验(2)

这题是决策单调性$dp$,我们把式子变形一下:

$$p_i=maxlimits^{n}_{j=1}{val_j+sqrt{|i-j|}}-a_i$$

 看这绝对值很不爽,我们可以正着扫一遍,再把序列翻转扫一遍,就解决了绝对值问题了

我们只讨论前一部分,设:

$$f_j=val_j+sqrt{i-j}$$

那么我们要求的就是这个函数的最值

显然,对于其中任意两个相邻的函数$f_t,f_{t+1}$,它们都有一个临界值$k_{t,t+1}$

显然序列中的$k_{1,2},k_{2,3}...$也要严格递增。否则,如果$k_{t,t+1}ge k_{t+1,t+2}$可以想象$f_{t+1}$根本没有用。

那么我们可以用队列模拟二分栈,代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define N 500007
#define Get(i,j) val[j]+sq[i-j]
using namespace std;
int n;
int val[N],que[N],k[N];
double sq[N],f[N];
int Search(int x,int y)
{
	int l=y,r=k[x]?k[x]:n,mid,ans=r+1;
	while(l<=r)
	{
		mid=l+((r-l)>>1);
		if(Get(mid,x)<=Get(mid,y))
			ans=mid,r=mid-1;
		else
			l=mid+1;
	}
//	cout<<"ans:"<<ans<<endl;
	return ans;
}
void Work()
{
	memset(k,0,sizeof(k));
	int head=1,tail=0;
//	memset(que,0,sizeof(que));
	for(int i=1;i<=n;++i)
	{
		while(head<tail&&k[head]<=i)
			++head;
		f[i]=max(f[i],(double)Get(i,que[head]));
		while(head<tail&&Get(k[tail-1],que[tail])<=Get(k[tail-1],i))
			--tail;
		k[tail]=Search(que[tail],i); que[++tail]=i; 
	//	cout<<"f[i]:"<<f[i]<<endl;
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
		scanf("%d",&val[i]),sq[i]=sqrt(i);
	Work();
//	printf("
");
//	for(int i=1;i<=n;++i)
//		printf("%d ",k[i]);
//	printf("
");
	reverse(val+1,val+1+n);
	reverse(f+1,f+1+n);
	Work();
//	printf("
");
//	for(int i=1;i<=n;++i)
//		printf("%d ",k[i]);
//	printf("
");
	for(int i=n;i>=1;--i)
		printf("%d
",max((int)ceil(f[i])-val[i],0));
	return 0;
}

  

原文地址:https://www.cnblogs.com/yexinqwq/p/10223621.html