AT_dp_z Frog 3 题解

23 年 8 月 6 日 星期日
243 字
2 分钟

大致题意

nn 个点,每个点有一个属性 hih_ihih_i 单调递增,从 ii 点移动到大于 iijj 点,需要付出 (hihj)2+c(h_i-h_j)^2+c 的代价,其中 cc 题中已给出,求点 11 移动到点 nn 的最小代价。

思路/解析

fif_i 表示从点 11 移动到点 ii 的最小代价,容易列出状态转移方程为 fi=minj<i(fj+(aiaj)2+c)f_i=\min_{j<i}(f_j+(a_i-a_j)^2+c)

直接转移的时间复杂度是 O(n2)O(n^2),无法通过本题,于是运用斜率优化,公式就变为了

fi=fj+ai22aiaj+aj2+c(fiai2)=(fj+aj2+c)(2ai)(aj)\begin{aligned}f_i&=f_j+a_i^2-2a_ia_j+a_j^2+c\\(f_i-a_i^2)&=(f_j+a_j^2+c)-(2a_i)(a_j)\end{aligned}

将状态 ii 视为平面上的点 (ai,fi+ai2+c)(a_i,f_i+a_i^2+c),问题就变成了求最小的斜率固定的直线的截距,因为 aia_i 单调递增,所以点的坐标也单调递增,斜率也单调递增。使用单调队列维护下凸壳,即可通过本题。

代码

cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=2e5+10;
ll n,c,i,l,r,h[N],q[N],dp[N];
double Y(ll j){return dp[j]+h[j]*h[j];}
double X(ll i){return h[i]<<1;}
double K(ll i,ll j){return (Y(i)-Y(j))/((X(i)-X(j)));}
int main(){
	cin>>n>>c;
	for(i=1;i<=n;i++)cin>>h[i];
	l=r=q[1]=1;
	for(i=2;i<=n;i++){
		while(l<r&&h[i]>=K(q[l],q[l+1]))l++;
		dp[i]=dp[q[l]]+c+(h[i]-h[q[l]])*(h[i]-h[q[l]]);
		while(l<r&&K(q[r-1],q[r])>=K(q[r],i))r--;
		q[++r]=i;
	}
	cout<<dp[n]<<'\n';
}

文章标题:AT_dp_z Frog 3 题解

文章作者:Walter_Fang

文章链接:https://blog.walterfang.us.kg/posts/solution-at_dp_z[复制]

最后修改时间:


商业转载请联系站长获得授权,非商业转载请注明本文出处及文章链接,您可以自由地在任何媒体以任何形式复制和分发作品,也可以修改和创作,但是分发衍生作品时必须采用相同的许可协议。
本文采用CC BY-NC-SA 4.0进行许可。