kmjp's blog

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

HackerRank HourRank 26 : C. Pair Sums

これをきっちり解けきれたのは良い。
https://www.hackerrank.com/contests/hourrank-26/challenges/pair-sums

問題

数列Xのスコアf(X)を以下のように定める。

  • X中各要素のペアに対し積を計算し、その総和を取ったもの。

ここで数列Aが与えられる。Aの連続した部分列A'のうちf(A')の最大値を求めよ。

解法

分割統治で解く。
Aを中心で2等分してB+Cと表現できるとする。
B内またはC内で構成される部分列については個別に探索する。
後は、求める部分列がBのsuffixとCのprefixにまたがるケースを考えよう。

BのsuffixをS、CのprefixをTと置く。
するとf(S+T) = sum(S)*sum(T)+f(S)+f(T)より、g(T,x)=sum(T)*x + f(T)という関数を考えるとf(S,T) = g(T,sum(s))+f(S)となる。
g(T,x)はxの1次式である。よって同じxに対しg(T,x)が最大となるTは、Convex Hull Trickの要領で求められる。

これで方針が決まった。各Tに対しg(T,x)=sum(T)*x + f(T)を求めておき、Convex Hull Trickでxに対しg(T,x)が最大となるケースを求めておこう。
あとはSの範囲を総当たりしつつg(T,sum(s))+f(S)の最大値を求めればよい。

int N;
ll A[505050];
ll ma;

template<typename V> struct ConvexHull {
	deque<pair<V,V>> Q;
	int cmptype=0; // 0-min 1-max
	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 cmptype^((B.second-C.second)*(B.first-A.first)<=(A.second-B.second)*(C.first-B.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();
	}
	void add(vector<pair<V,V>> v) {
		sort(v.begin(),v.end());
		if(cmptype==1) reverse(v.begin(),v.end());
		for(auto r=v.begin();r!=v.end();r++) add(r->first,r->second);
	}
	
	
	V query(V x) {
		int L=-1,R=Q.size()-1;
		while(R-L>1) {
			int M=(L+R)/2;
			(cmptype^((calc(Q[M],x)<=calc(Q[M+1],x)))?L:R)=M;
		}
		return calc(Q[R],x);
	}
};

void dfs(int L,int R) {
	if(R-L<=1) return ;
	if(R-L==2) {
		ma=max(ma,A[L]*A[L+1]);
		return;
	}
	
	int M=L+(R-L)/2;
	dfs(L,M);
	dfs(M,R);
	vector<pair<ll,ll>> V;
	ll a=0,b=0;
	int i;
	for(i=M;i<R;i++) {
		b+=A[i]*a;
		a+=A[i];
		V.push_back({a,b});
	}
	sort(ALL(V));
	
	ConvexHull<ll> ch;
	FORR(v,V) {
		ch.add(v.first,v.second);
	}
	
	a=0,b=0;
	for(i=M-1;i>=L;i--) {
		b+=A[i]*a;
		a+=A[i];
		ma=max(ma,ch.query(a)+b);
	}
}

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>N;
	FOR(i,N) cin>>A[i];
	
	dfs(0,N);
	
	cout<<ma<<endl;
	
}

まとめ

連続部分列に関する問題は分割統治がよく効くね。