kmjp's blog

競技プログラミング参加記です

Educational DP Contest : Z - Frog 3

昔は苦手だったけどこれはだいぶ慣れてきた。
https://atcoder.jp/contests/dp/tasks/dp_z

問題

N個の足場があり、それぞれの高さはH[i]である。
i番の足場から、より番号の大きいj番の足場に移動するときのコストは、定数値Cを用いて(H[i]-H[j])^2+Cと計算される。
1番の足場からN番に移る最小コストを求めよ。

解法

Convex Hull Trickで解く。
dp(i) := i番の足場に至る最小コスト
とすると、
dp(j) = min(dp(i) + (H[i]-H[j])^2+C)だが、dp(i) + (H[i]-H[j])^2+CはH[j]^2 - 2*H[i]*H[j] + H[i]^2+Cと変形し、H[j]は固定と考えるとxの係数が2*H[i]であるような多数の1次式の最小値を取る問題とみなせる。
あとはConvex Hull Trickを使っていこう。
以下類題。
yukicoder : No.703 ゴミ拾い Easy - kmjp's blog

template<typename V> struct ConvexHull {
	deque<pair<V,V>> Q;
	V calc(pair<V,V> p, V x) {
		return p.first*x+p.second;
	}
	int dodo(pair<V,V> A,pair<V,V> B, pair<V,V> C) { // max or min
		return ((A.second-C.second)*(B.first-A.first)<=(A.second-B.second)*(C.first-A.first));
	}
	void add(V a, V b) { // add ax+b
		Q.push_back({a,b});
		int v;
		while((v=Q.size())>=3 && dodo(Q[v-3],Q[v-2],Q[v-1]))
			Q[v-2]=Q[v-1], Q.pop_back();
	}
	V query(V x) {
		int L=-1,R=Q.size()-1;
		while(R-L>1) {
			int M=(L+R)/2;
			(((calc(Q[M],x)>=calc(Q[M+1],x)))?L:R)=M;
		}
		return calc(Q[R],x);
	}
};

int N;
ll C,H[202020],R[202020];
ConvexHull<ll> cht;

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>N>>C;
	FOR(i,N) {
		cin>>H[i];
		if(i) {
			R[i]=cht.query(H[i])+H[i]*H[i];
		}
		cht.add(-2*H[i],H[i]*H[i]+R[i]+C);
	}
	
	cout<<R[N-1]<<endl;
}

まとめ

ここ数年でメジャーになった競プロのテクニックってあまり思い浮かばないけど、あえて言うとCHTな印象。