これをきっちり解けきれたのは良い。
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; }
まとめ
連続部分列に関する問題は分割統治がよく効くね。