雅礼集训 Day2 T1 施工

版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载,欢迎添加友链。 https://blog.csdn.net/zzk_233/article/details/83311421

首先有一个比较好想的结论(谁说的。。。一点都不好想!)就是最优的情况一定是有两端高于中间的一段平地。

因为一段本来有高度差的一起增高等于没用,所以我们可以把最终高度相等的作为一段,无论这段有多少个。

而这样做的条件是两边比中间高。

这样我们得到一个dp式子f_{i}=\sum_{k=j+1}^{i-1}*(t-val_{k})^{2}+c(h_{i}+h_{j}-2*t),这个是个二次函数,可以直接求出t的最小值

就是理想高度,这样就求出了最小的f_{i}来更新,而对于j的选取,就是在i前面第一个比它高的为止,因为维护一个单调递减的

序列,如果拿出的点比i小,那么因为这个点前面的比这个点大(单调递减),所以可以作为j和i作为一组,当这个点比i高时

就不合法了 ,用单调队列可以优化掉正常要枚举的三维。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int n;
ll c;
ll val[1000006];
ll ss[2][1000006];
int stak[1000006],top;
ll dp[1000006];
ll gett(int x,int y,ll mx)
{
	ll a=y-x-1;
	ll b=-2*(ss[0][y-1]-ss[0][x])-(x!=0)*1ll*c-(y!=n+1)*1ll*c;
	ll cc=ss[1][y-1]-ss[1][x]+(x!=0)*1ll*val[x]*c+(y!=n+1)*1ll*val[y]*c;
	ll t=-1*(ll)floor((double)b/((double)a*2.0)+0.5);
	t=max(t,mx);
	if(x!=0)t=min(t,val[x]);
	if(y!=n+1)t=min(t,val[y]);
	return a*t*t+b*t+cc;
}
int main()
{
	freopen("construct.in","r",stdin);
	freopen("construct.out","w",stdout);
	scanf("%d%I64d",&n,&c);
	for(int i=1;i<=n;i++)
	{
		scanf("%I64d",&val[i]);
		ss[0][i]=ss[0][i-1]+val[i];
		ss[1][i]=ss[1][i-1]+val[i]*val[i];
	}
	val[0]=val[n+1]=0x3f3f3f3f;
	stak[top++]=0;
	for(int i=1;i<=n+1;i++)
	{
		//dp[i]=dp[i-1]+(i==1||i==n+1)?0:abs(val[i]-val[i-1])*c;有毒的三步运算符 
		if(i==1||i==n+1)
		{
			dp[i]=dp[i-1];
		}else
		{
			dp[i]=dp[i-1]+abs(val[i]-val[i-1])*c;
		}
		while(top>0&&val[stak[top-1]]<=val[i])
		{
			if(top>1)
			{
				dp[i]=min(dp[i],dp[stak[top-2]]+gett(stak[top-2],i,val[stak[top-1]]));
			}
			top--;
		}
		stak[top++]=i;
	}
	printf("%I64d",dp[n+1]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zzk_233/article/details/83311421
今日推荐