Problem:
Score : 600 points

Problem Statement

Snuke has an integer sequence A of length N.

He will make three cuts in A and divide it into four (non-empty) contiguous subsequences B, C, D and E.
The positions of the cuts can be freely chosen.

Let P,Q,R,S be the sums of the elements in B,C,D,E, respectively.
Snuke is happier when the absolute difference of the maximum and the minimum among P,Q,R,S is smaller.
Find the minimum possible absolute difference of the maximum and the minimum among P,Q,R,S.

Constraints

4 \leq N \leq 2 \times 10^5

1 \leq A_i \leq 10^9

All values in input are integers.

Input

Input is given from Standard Input in the following format:

N
A_1 A_2 ... A_N

Output

Find the minimum possible absolute difference of the maximum and the minimum among P,Q,R,S.

Sample Input 1

5
3 2 4 1 2

Sample Output 1

2

If we divide A as B,C,D,E=(3),(2),(4),(1,2), then P=3,Q=2,R=4,S=1+2=3.
Here, the maximum and the minimum among P,Q,R,S are 4 and 2, with the absolute difference of 2.
We cannot make the absolute difference of the maximum and the minimum less than 2, so the answer is 2.

Sample Input 2

10
10 71 84 33 6 47 23 25 52 64

Sample Output 2

36

Sample Input 3

7
1 2 3 1000000000 4 5 6

Sample Output 3

999999994

Solution in C# (Mono 4.6.2.0) (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using static System.Console;
using static PB10004.Algorithm.Util;

namespace PB10004.Algorithm
{
    class Template
    {
        static void Main(string[] args)
        {
            var N = ReadLong();
            var array = ReadLongArray();

            var cum = CumulativeSum(array).ToArray();

            long[] D1 = new long[N];
            long i = 0;
            long c = 0;
            while (i < N)
            {
                if (IsBestL(i, c, cum, N))
                {
                    D1[i] = c;
                    i++;
                }
                else
                    c++;
            }

            long[] D2 = new long[N];
            i = N - 1;
            c = N - 1;
            while (i >= 0)
            {
                if (IsBestR(i, c, cum, N))
                {
                    D2[i] = c;
                    i--;
                }
                else
                    c--;
            }
            long best = long.MaxValue;
            for (i = 0; i < N; i++)
            {
                long c1 = D1[i];
                long c2 = i;
                long c3 = D2[i];
                long c4 = N - 1;
                long p1 = cum[c1];
                long p2 = cum[c2] - cum[c1];
                long p3 = cum[c3] - cum[c2];
                long p4 = cum[c4] - cum[c3];

                long mn = Math.Min(p1, Math.Min(p2, Math.Min(p3, p4)));
                long mx = Math.Max(p1, Math.Max(p2, Math.Max(p3, p4)));
                best = Math.Min(best, mx - mn);
            }
            WriteLine(best);
        }
        static bool IsBestL(long i, long c, long[] S, long N)
        {
            if (c == i) return true;
            long s = S[i];
            long sc = S[c];
            long sn = S[c + 1];
            long current = Math.Abs(s - 2 * sc);
            long next = Math.Abs(s - 2 * sn);
            return current <= next;
        }

        static bool IsBestR(long i, long c, long[] S, long N)
        {
            if (c == i) return true;
            long s = S[N - 1] - S[i];
            long sc = S[N - 1] - S[c];
            long sn = S[N - 1] - S[c - 1];
            long current = Math.Abs(s - 2 * sc);
            long next = Math.Abs(s - 2 * sn);
            return current <= next;
        }
    }
    public static class Util
    {
        /// <summary>
        /// 標準入力をlong型にパースする
        /// </summary>
        /// <returns></returns>
        public static long ReadLong()
        {
            return long.Parse(ReadLine());
        }
        /// <summary>
        /// 標準入力をlong型の配列にする
        /// </summary>
        /// <returns></returns>
        public static long[] ReadLongArray()
        {
            return ReadLine().Split(' ').Select(long.Parse).ToArray();
        }
        /// <summary>
        /// 一定値の数列を生成する
        /// </summary>
        /// <param name="value"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static IEnumerable<long> ConstSeq(long value, long length)
        {
            for (int i = 0; i < length; i++)
            {
                yield return value;
            }
        }
        /// <summary>
        /// int配列の累積和を求める
        /// </summary>
        /// <param name="array"></param>
        /// <returns></returns>
        public static IEnumerable<int> CumulativeSum(IEnumerable<int> array)
        {
            int tmp = 0;
            foreach (var num in array)
                yield return tmp += num;
        }
        /// <summary>
        /// long配列の累積和を求める
        /// </summary>
        /// <param name="array"></param>
        /// <returns></returns>
        public static IEnumerable<long> CumulativeSum(IEnumerable<long> array)
        {
            long tmp = 0;
            foreach (var num in array)
                yield return tmp += num;
        }
        /// <summary>
        /// 最大公約数をユークリッドの互助法で求める
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static long Gcd(long a, long b)
        {
            return b == 0 ? a : Gcd(b, a % b);
        }
        /// <summary>
        /// 最小公倍数を求める
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static long Lcm(long a, long b)
        {
            return a * b / Gcd(a, b);
        }
    }
}